diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 73d369353e..1eaf05f5cd 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -24,8 +24,8 @@ # therefore, unfortunatelly, the installation cannot be copied/moved elsewhere without re-installing wxWidgets. # +cmake_minimum_required(VERSION 3.10) project(PrusaSlicer-deps) -cmake_minimum_required(VERSION 3.2) include(ExternalProject) include(ProcessorCount) @@ -66,6 +66,10 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE) message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.") endif () +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + cmake_policy(SET CMP0135 NEW) +endif () + function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) @@ -199,6 +203,8 @@ include(NanoSVG/NanoSVG.cmake) include(wxWidgets/wxWidgets.cmake) include(OCCT/OCCT.cmake) +include(LibBGCode/LibBGCode.cmake) + set(_dep_list dep_Boost dep_TBB @@ -214,6 +220,7 @@ set(_dep_list ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} + dep_LibBGCode ) # if (NOT MSVC) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake new file mode 100644 index 0000000000..92003869a6 --- /dev/null +++ b/deps/LibBGCode/LibBGCode.cmake @@ -0,0 +1,34 @@ +set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") + +set(_source_dir_line + URL https://github.com/prusa3d/libbgcode/archive/50bedae2ae0c7fc83dd350a8be99ddc8f1749005.zip + URL_HASH SHA256=3958c93a325d6d7ed1c97aabb37cc09a08f8e981e3a7917312d568071e462162 +) + +if (LibBGCode_SOURCE_DIR) + set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") +endif () + +prusaslicer_add_cmake_project(LibBGCode_deps + ${_source_dir_line} + SOURCE_SUBDIR deps + DEPENDS dep_Boost ${ZLIB_PKG} + CMAKE_ARGS + -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} + -DDEP_CMAKE_OPTS:STRING=-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON + -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF + -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON + -DDESTDIR=${DESTDIR} +) + +prusaslicer_add_cmake_project(LibBGCode + ${_source_dir_line} + DEPENDS dep_LibBGCode_deps + CMAKE_ARGS + -DLibBGCode_BUILD_TESTS:BOOL=OFF + -DLibBGCode_BUILD_CMD_TOOL:BOOL=OFF +) + +if (MSVC) + add_debug_dep(dep_LibBGCode) +endif () \ No newline at end of file diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index f771c70b32..0f8b376442 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -25,7 +25,6 @@ our @EXPORT_OK = qw( X Y Z convex_hull - chained_path_from deg2rad rad2deg ); diff --git a/resources/icons/convert_file.svg b/resources/icons/convert_file.svg new file mode 100644 index 0000000000..2de2b707f0 --- /dev/null +++ b/resources/icons/convert_file.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/scalar_param.svg b/resources/icons/scalar_param.svg new file mode 100644 index 0000000000..f9386ab70e --- /dev/null +++ b/resources/icons/scalar_param.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/vector_filament_param.svg b/resources/icons/vector_filament_param.svg new file mode 100644 index 0000000000..a3404cfd88 --- /dev/null +++ b/resources/icons/vector_filament_param.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/vector_param.svg b/resources/icons/vector_param.svg new file mode 100644 index 0000000000..a5c8affc7f --- /dev/null +++ b/resources/icons/vector_param.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/localization/list.txt b/resources/localization/list.txt index de553f2a32..df800c95a6 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -33,6 +33,7 @@ src/slic3r/GUI/DesktopIntegrationDialog.cpp src/slic3r/GUI/DoubleSlider.cpp src/slic3r/GUI/Downloader.cpp src/slic3r/GUI/DownloaderFileGet.cpp +src/slic3r/GUI/EditGCodeDialog.cpp src/slic3r/GUI/ExtraRenderers.cpp src/slic3r/GUI/ExtruderSequenceDialog.cpp src/slic3r/GUI/Field.cpp diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 09bd30991a..38535b192f 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,6 @@ +min_slic3r_version = 2.6.2-alpha0 +1.11.0-alpha1 Updated ramming parameters. Updated start-gcode for XL Multi-Tool. Updated output filename format. +1.11.0-alpha0 Binary g-code, arc fitting, QOI/PNG thumbnails, 90degree XL tower, XL specific filament variants. min_slic3r_version = 2.6.0-beta2 1.9.8 FW version notification (MK2.5/3 family). Minor update of MK4IS profiles. Updated MK4IS thumbnail. 1.9.7 MK4 Input Shaper RC firmware notification. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 93d05a216d..e81382c039 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.9.8 +config_version = 1.11.0-alpha1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -33,6 +33,16 @@ bed_texture = mk4.svg thumbnail = MK4IS_thumbnail_v2.png default_materials = Prusament PLA @PGIS; Prusament PLA Blend @PGIS; Prusament PETG @PGIS; Generic PLA @PGIS; Prusa PLA @PGIS; Prusa PETG @PGIS; Generic ASA @MK4; Generic PETG @PGIS; Prusa PLA @PGIS; Generic PLA Silk @PGIS +; [printer_model:MK3.9] +; name = Original Prusa MK3.9 +; variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8 +; technology = FFF +; family = MK3.9 +; bed_model = mk4_bed.stl +; bed_texture = mk4.svg +; thumbnail = MK4IS_thumbnail_v2.png +; default_materials = Prusament PLA @PGIS; Prusament PLA Blend @PGIS; Prusament PETG @PGIS; Generic PLA @PGIS; Prusa PLA @PGIS; Prusa PETG @PGIS; Generic ASA @MK4; Generic PETG @PGIS; Prusa PLA @PGIS; Generic PLA Silk @PGIS + [printer_model:MINI] name = Original Prusa MINI && MINI+ variants = 0.4; 0.25; 0.6; 0.8 @@ -94,7 +104,7 @@ technology = FFF family = XL Single-Tool bed_model = xl_bed.stl bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6 +default_materials = Generic PLA @XL 0.6; Generic ABS @XL 0.6; Generic PETG @XL 0.6; Prusament PLA @XL 0.6; Prusament PETG @XL 0.6; Prusament ASA @XL 0.6; Prusament PC Blend @XL 0.6; Prusament PC Blend Carbon Fiber @XL 0.6; Prusament PVB @XL 0.6; Prusament PA11 Carbon Fiber @XL 0.6 [printer_model:XL2] name = Original Prusa XL - 2T @@ -103,7 +113,7 @@ technology = FFF family = XL Multi-Tool bed_model = xl_bed.stl bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6; Verbatim BVOH @PG 0.6 +default_materials = Generic PLA @XL 0.6; Generic ABS @XL 0.6; Generic PETG @XL 0.6; Prusament PLA @XL 0.6; Prusament PETG @XL 0.6; Prusament ASA @XL 0.6; Prusament PC Blend @XL 0.6; Prusament PC Blend Carbon Fiber @XL 0.6; Prusament PVB @XL 0.6; Prusament PA11 Carbon Fiber @XL 0.6; Verbatim BVOH @XL 0.6 [printer_model:XL5] name = Original Prusa XL - 5T @@ -112,7 +122,7 @@ technology = FFF family = XL Multi-Tool bed_model = xl_bed.stl bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6; Verbatim BVOH @PG 0.6 +default_materials = Generic PLA @XL 0.6; Generic ABS @XL 0.6; Generic PETG @XL 0.6; Prusament PLA @XL 0.6; Prusament PETG @XL 0.6; Prusament ASA @XL 0.6; Prusament PC Blend @XL 0.6; Prusament PC Blend Carbon Fiber @XL 0.6; Prusament PVB @XL 0.6; Prusament PA11 Carbon Fiber @XL 0.6; Verbatim BVOH @XL 0.6 [printer_model:MK2.5S] name = Original Prusa i3 MK2.5S @@ -238,7 +248,7 @@ notes = overhangs = 1 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{nozzle_diameter[initial_tool]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode perimeters = 2 perimeter_extruder = 1 perimeter_extrusion_width = 0.45 @@ -332,14 +342,17 @@ first_layer_speed = 25 support_material_threshold = 45 raft_first_layer_density = 80% ## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; -output_filename_format = {input_filename_base}_{nozzle_diameter[initial_tool]}n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode wipe_tower_cone_angle = 25 +wipe_tower_rotation_angle = 90 wipe_tower = 1 wipe_tower_bridging = 8 -wipe_tower_extra_spacing = 100 +wipe_tower_extra_spacing = 150 wipe_tower_brim_width = 3 ooze_prevention = 1 standby_temperature_delta = -110 +gcode_binary = 1 +arc_fitting = emit_radius [print:*MK4*] inherits = *common* @@ -363,6 +376,9 @@ first_layer_speed = 20 support_material_threshold = 45 raft_first_layer_density = 80% ## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +gcode_binary = 1 +arc_fitting = emit_radius [print:*MK306*] inherits = *MK3* @@ -403,6 +419,7 @@ thick_bridges = 0 bridge_flow_ratio = 1 bridge_speed = 20 wipe_tower_bridging = 6 +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -433,6 +450,10 @@ infill_acceleration = 800 first_layer_acceleration = 500 [print:*0.25nozzleXL*] +inherits = *0.25nozzleMK4* +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode + +[print:*0.25nozzleMK4*] inherits = *0.25nozzleMK3* infill_speed = 40 solid_infill_speed = 40 @@ -441,9 +462,7 @@ first_layer_acceleration = 500 infill_anchor = 1 perimeters = 3 brim_separation = 0 - -[print:*0.25nozzleMK4*] -inherits = *0.25nozzleXL* +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode [print:*0.3nozzle*] external_perimeter_extrusion_width = 0.33 @@ -457,6 +476,14 @@ support_material_extrusion_width = 0.3 fill_density = 20% perimeters = 3 infill_anchor = 1.5 +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode + +[print:*0.3nozzleXL*] +inherits = *0.3nozzle* +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode + +[print:*0.4nozzleXL*] +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode [print:*0.5nozzle*] external_perimeter_extrusion_width = 0.55 @@ -476,6 +503,11 @@ infill_anchor = 2 infill_anchor_max = 15 thick_bridges = 0 bridge_speed = 30 +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode + +[print:*0.5nozzleXL*] +inherits = *0.5nozzle* +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 @@ -497,6 +529,7 @@ bridge_flow_ratio = 0.95 bridge_speed = 25 infill_overlap = 15% support_tree_branch_diameter_double_wall = 0 +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode [print:*0.6nozzleMK3*] inherits = *0.6nozzle* @@ -506,6 +539,10 @@ infill_extrusion_width = 0.65 thick_bridges = 0 [print:*0.6nozzleXL*] +inherits = *0.6nozzleMK4* +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode + +[print:*0.6nozzleMK4*] inherits = *0.6nozzle* external_perimeter_extrusion_width = 0.65 extrusion_width = 0.65 @@ -514,9 +551,7 @@ thick_bridges = 0 fill_density = 20% support_material_interface_spacing = 0.25 infill_anchor = 2.5 - -[print:*0.6nozzleMK4*] -inherits = *0.6nozzleXL* +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode [print:*0.6nozzleMINI*] inherits = *0.6nozzleMK3* @@ -573,6 +608,7 @@ single_extruder_multi_material_priming = 0 thick_bridges = 1 overhangs = 0 support_tree_branch_diameter_double_wall = 0 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode [print:*0.8nozzleXL*] inherits = *0.8nozzle* @@ -587,12 +623,14 @@ raft_first_layer_expansion = 2 default_acceleration = 1250 infill_anchor = 2.5 first_layer_acceleration = 500 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode [print:*0.8nozzleMK4*] inherits = *0.8nozzleXL* default_acceleration = 1000 infill_acceleration = 2000 first_layer_acceleration = 600 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode [print:*soluble_support*] overhangs = 1 @@ -1949,7 +1987,7 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]== ## XL - 0.3mm nozzle [print:0.05mm ULTRADETAIL @XL 0.3] -inherits = *0.05mm*; *XL*; *0.3nozzle* +inherits = *0.05mm*; *XL*; *0.3nozzleXL* top_solid_layers = 14 bottom_solid_layers = 9 support_material_contact_distance = 0.07 @@ -1975,7 +2013,7 @@ default_acceleration = 800 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.08mm DETAIL @XL 0.3] -inherits = *0.07mm*; *XL*; *0.3nozzle* +inherits = *0.07mm*; *XL*; *0.3nozzleXL* layer_height = 0.08 support_material_contact_distance = 0.08 raft_contact_distance = 0.08 @@ -2003,7 +2041,7 @@ gcode_resolution = 0.006 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.12mm QUALITY @XL 0.3] -inherits = *0.12mm*; *XL*; *0.3nozzle* +inherits = *0.12mm*; *XL*; *0.3nozzleXL* support_material_contact_distance = 0.1 raft_contact_distance = 0.1 perimeter_speed = 35 @@ -2029,7 +2067,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.16mm SPEED @XL 0.3] -inherits = *0.16mm*; *XL*; *0.3nozzle* +inherits = *0.16mm*; *XL*; *0.3nozzleXL* support_material_contact_distance = 0.15 raft_contact_distance = 0.15 perimeter_speed = 50 @@ -2055,7 +2093,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.20mm DRAFT @XL 0.3] -inherits = *0.20mm*; *XL*; *0.3nozzle* +inherits = *0.20mm*; *XL*; *0.3nozzleXL* support_material_contact_distance = 0.18 raft_contact_distance = 0.18 perimeter_speed = 50 @@ -2088,7 +2126,7 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]== ## XL - 0.4mm nozzle [print:0.07mm ULTRADETAIL @XL 0.4] -inherits = *0.07mm*; *XL* +inherits = *0.07mm*; *XL*; *0.4nozzleXL* thick_bridges = 1 bridge_flow_ratio = 0.6 top_infill_extrusion_width = 0.4 @@ -2123,7 +2161,7 @@ default_acceleration = 800 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.10mm DETAIL @XL 0.4] -inherits = *0.10mm*; *XL* +inherits = *0.10mm*; *XL*; *0.4nozzleXL* support_material_contact_distance = 0.17 raft_contact_distance = 0.15 perimeter_speed = 45 @@ -2159,7 +2197,7 @@ gcode_resolution = 0.006 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.15mm QUALITY @XL 0.4] -inherits = *0.15mm*; *XL* +inherits = *0.15mm*; *XL*; *0.4nozzleXL* perimeter_speed = 65 external_perimeter_speed = 40 small_perimeter_speed = 35 @@ -2191,7 +2229,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.20mm QUALITY @XL 0.4] -inherits = *0.20mm*; *XL* +inherits = *0.20mm*; *XL*; *0.4nozzleXL* perimeter_speed = 65 external_perimeter_speed = 40 small_perimeter_speed = 35 @@ -2223,7 +2261,7 @@ top_infill_extrusion_width = 0.4 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.20mm SOLUBLE INTERFACE @XL 0.4] -inherits = 0.20mm QUALITY @XL 0.4; *soluble_support_XL* +inherits = 0.20mm QUALITY @XL 0.4; *soluble_support_XL*; *0.4nozzleXL* support_material_extruder = 0 perimeter_speed = 50 external_perimeter_speed = 35 @@ -2240,7 +2278,7 @@ support_material_extruder = 2 support_material_with_sheath = 1 [print:0.20mm SPEED @XL 0.4] -inherits = *0.20mm*; *XL* +inherits = *0.20mm*; *XL*; *0.4nozzleXL* perimeter_speed = 90 external_perimeter_speed = 70 small_perimeter_speed = 40 @@ -2271,7 +2309,7 @@ top_infill_extrusion_width = 0.42 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.30mm DRAFT @XL 0.4] -inherits = *0.30mm*; *XL* +inherits = *0.30mm*; *XL*; *0.4nozzleXL* bottom_solid_layers = 3 perimeter_speed = 80 external_perimeter_speed = 70 @@ -2310,7 +2348,7 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]== ## XL - 0.5mm nozzle [print:0.10mm DETAIL @XL 0.5] -inherits = *0.10mm*; *XL*; *0.5nozzle* +inherits = *0.10mm*; *XL*; *0.5nozzleXL* perimeter_speed = 40 external_perimeter_speed = 30 small_perimeter_speed = 25 @@ -2341,7 +2379,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.5 [print:0.15mm QUALITY @XL 0.5] -inherits = *0.15mm*; *XL*; *0.5nozzle* +inherits = *0.15mm*; *XL*; *0.5nozzleXL* perimeter_speed = 65 external_perimeter_speed = 40 small_perimeter_speed = 35 @@ -2365,7 +2403,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.5 [print:0.20mm QUALITY @XL 0.5] -inherits = 0.15mm QUALITY @XL 0.5; *0.20mm*; *XL*; *0.5nozzle* +inherits = 0.15mm QUALITY @XL 0.5; *0.20mm*; *XL*; *0.5nozzleXL* gcode_resolution = 0.01 support_material_interface_layers = 4 infill_speed = 200 @@ -2396,7 +2434,7 @@ support_material_with_sheath = 1 support_material_extrusion_width = 0.47 [print:0.25mm SPEED @XL 0.5] -inherits = *0.25mm*; *XL*; *0.5nozzle* +inherits = *0.25mm*; *XL*; *0.5nozzleXL* bottom_solid_layers = 3 perimeter_speed = 70 external_perimeter_speed = 70 @@ -2422,7 +2460,7 @@ max_print_speed = 200 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.5 [print:0.32mm DRAFT @XL 0.5] -inherits = *0.32mm*; *XL*; *0.5nozzle* +inherits = *0.32mm*; *XL*; *0.5nozzleXL* bottom_solid_layers = 3 perimeter_speed = 70 external_perimeter_speed = 70 @@ -3730,7 +3768,7 @@ min_skirt_length = 4 mmu_segmented_region_max_width = 0 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode overhang_speed_0 = 15 overhang_speed_1 = 25 overhang_speed_2 = 30 @@ -3800,7 +3838,9 @@ travel_speed = 300 travel_speed_z = 12 thick_bridges = 0 thin_walls = 0 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.4 +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.4 +gcode_binary = 1 +arc_fitting = emit_radius [print:*MK4IS_common025*] inherits = *MK4IS_common* @@ -3840,7 +3880,8 @@ raft_first_layer_density = 95% gap_fill_speed = 50 single_extruder_multi_material_priming = 0 wipe_tower = 1 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.25 +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.25 [print:*MK4IS_common03*] inherits = *MK4IS_common* @@ -3875,7 +3916,8 @@ raft_first_layer_density = 90% gap_fill_speed = 50 top_solid_min_thickness = 0.7 bottom_solid_min_thickness = 0.5 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.3 +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.3 [print:*MK4IS_common05*] inherits = *MK4IS_common* @@ -3902,7 +3944,8 @@ infill_acceleration = 4000 default_acceleration = 2500 infill_anchor = 2 infill_anchor_max = 15 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.5 +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.5 [print:*MK4IS_common06*] inherits = *MK4IS_common* @@ -3923,7 +3966,8 @@ support_material_interface_spacing = 0.25 support_material_interface_speed = 75% raft_contact_distance = 0.25 gap_fill_speed = 70 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 overhang_speed_0 = 15 overhang_speed_1 = 20 overhang_speed_2 = 25 @@ -3956,7 +4000,8 @@ raft_contact_distance = 0.2 gap_fill_speed = 40 top_solid_min_thickness = 1.2 bottom_solid_min_thickness = 0.8 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 seam_position = nearest infill_anchor = 2.5 infill_anchor_max = 20 @@ -4633,7 +4678,7 @@ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ a [filament:*PLAPG*] start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.038{elsif nozzle_diameter[0]==0.5}0.025{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.014{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 slowdown_below_layer_time = 8 filament_cooling_final_speed = 2 filament_cooling_initial_speed = 3 @@ -4651,16 +4696,39 @@ full_fan_speed_layer = 3 [filament:*PLA06PG*] inherits = *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.6 filament_max_volumetric_speed = 15.5 slowdown_below_layer_time = 14 [filament:*PLA08PG*] inherits = *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.8 filament_max_volumetric_speed = 19 slowdown_below_layer_time = 18 +[filament:*PLAXL*] +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +filament_multitool_ramming = 0 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 15 +filament_retract_length_toolchange = nil + +[filament:*PLA06XL*] +inherits = *PLAXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.6 + +[filament:*PLA08XL*] +inherits = *PLAXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.8 + +[filament:*PVAXL*] +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 10 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 + [filament:*PET*] inherits = *common* bed_temperature = 90 @@ -4688,7 +4756,7 @@ compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2S filament_max_volumetric_speed = 15 [filament:*PETPG*] -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 filament_max_volumetric_speed = 10 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.055{elsif nozzle_diameter[0]==0.5}0.042{elsif nozzle_diameter[0]==0.6}0.025{elsif nozzle_diameter[0]==0.8}0.018{elsif nozzle_diameter[0]==0.25}0.18{elsif nozzle_diameter[0]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" filament_cooling_final_speed = 1 @@ -4710,23 +4778,42 @@ slowdown_below_layer_time = 9 [filament:*PET06PG*] inherits = *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.6 filament_max_volumetric_speed = 17 slowdown_below_layer_time = 14 filament_retract_length = 0.9 [filament:*PET08PG*] inherits = *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.8 filament_max_volumetric_speed = 22 slowdown_below_layer_time = 18 filament_retract_length = 0.8 +[filament:*PETXL*] +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 + +[filament:*PET06XL*] +inherits = *PETXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.6 + +[filament:*PET08XL*] +inherits = *PETXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.8 + [filament:*04PLUS*] compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/ [filament:*04PLUSPG*] -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*MK4.*/ + +[filament:*04PLUSXL*] +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*XL.*/ [filament:*PETMMU1*] filament_retract_length = nil @@ -4837,7 +4924,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*ABSPG*] -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 filament_max_volumetric_speed = 12 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.04{elsif nozzle_diameter[0]==0.25}0.1{elsif nozzle_diameter[0]==0.3}0.06{elsif nozzle_diameter[0]==0.35}0.05{elsif nozzle_diameter[0]==0.5}0.03{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.02{elsif nozzle_diameter[0]==0.5}0.018{elsif nozzle_diameter[0]==0.6}0.012{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.25}0.09{elsif nozzle_diameter[0]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp" filament_cooling_final_speed = 50 @@ -4855,97 +4942,105 @@ idle_temperature = 100 [filament:*ABS06PG*] inherits = *ABSPG* filament_max_volumetric_speed = 15 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:*ABS08PG*] inherits = *ABSPG* filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 slowdown_below_layer_time = 25 -[filament:*ABSMK4*] -inherits = *ABSPG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +[filament:*ABSXL*] +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 -[filament:*ABS06MK4*] -inherits = *ABSMK4* -filament_max_volumetric_speed = 15 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 +[filament:*ABS06XL*] +inherits = *ABSXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*ABS08MK4*] -inherits = *ABSMK4* +[filament:*ABS08XL*] +inherits = *ABSXL* filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 slowdown_below_layer_time = 25 [filament:*PCPG*] inherits = *ABSPG* filament_max_volumetric_speed = 8 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" -first_layer_bed_temperature = 100 -bed_temperature = 105 -idle_temperature = 150 filament_minimal_purge_on_wipe_tower = 35 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:*PC06PG*] inherits = *PCPG* filament_max_volumetric_speed = 14 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:*PC08PG*] inherits = *PCPG* filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 -[filament:*PCMK4*] -inherits = *ABSMK4* -filament_max_volumetric_speed = 8 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" +[filament:*PCXL*] +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 idle_temperature = 150 +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 -[filament:*PC06MK4*] -inherits = *PCMK4* -filament_max_volumetric_speed = 14 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 +[filament:*PC06XL*] +inherits = *PCXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*PC08MK4*] -inherits = *PCMK4* -filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 +[filament:*PC08XL*] +inherits = *PCXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 [filament:*PAPG*] inherits = *ABSPG* filament_max_volumetric_speed = 5 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" -bed_temperature = 105 idle_temperature = 150 filament_minimal_purge_on_wipe_tower = 35 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:*PA06PG*] inherits = *PAPG* filament_max_volumetric_speed = 7 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:*PA08PG*] inherits = *PAPG* filament_max_volumetric_speed = 10 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 -[filament:*PAMK4*] -inherits = *ABSMK4* -filament_max_volumetric_speed = 5 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" +[filament:*PAXL*] +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 idle_temperature = 70 +bed_temperature = 105 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 -[filament:*PA06MK4*] -inherits = *PAMK4* +[filament:*PA06XL*] +inherits = *PAXL* filament_max_volumetric_speed = 7 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*PA08MK4*] -inherits = *PAMK4* +[filament:*PA08XL*] +inherits = *PAXL* filament_max_volumetric_speed = 10 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 [filament:*FLEX*] inherits = *common* @@ -4973,6 +5068,25 @@ filament_retract_lift = 0 filament_wipe = 0 [filament:*FLEXPG*] +filament_max_volumetric_speed = 4 +filament_retract_speed = 60 +filament_deretract_speed = 20 +filament_retract_before_travel = 2 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +idle_temperature = 70 +start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" + +[filament:*FLEX06PG*] +inherits = *FLEXPG* +filament_max_volumetric_speed = 6.5 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 + +[filament:*FLEX08PG*] +inherits = *FLEXPG* +filament_max_volumetric_speed = 9 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 + +[filament:*FLEXXL*] filament_max_volumetric_speed = 3.5 filament_retract_speed = 60 filament_deretract_speed = 20 @@ -4980,37 +5094,23 @@ filament_retract_before_travel = 2 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 idle_temperature = 70 start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" +filament_multitool_ramming = 0 +filament_multitool_ramming_volume = 40 +filament_multitool_ramming_flow = 3.5 filament_minimal_purge_on_wipe_tower = 35 -[filament:*FLEX06PG*] -inherits = *FLEXPG* +[filament:*FLEX06XL*] +inherits = *FLEXXL* filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*FLEX08PG*] -inherits = *FLEXPG* +[filament:*FLEX08XL*] +inherits = *FLEXXL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 -[filament:*FLEXMK4*] -filament_max_volumetric_speed = 4 -filament_retract_speed = 60 -filament_deretract_speed = 20 -filament_retract_before_travel = 2 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 -idle_temperature = 70 -start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" - -[filament:*FLEX06MK4*] -inherits = *FLEXMK4* -filament_max_volumetric_speed = 6.5 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 - -[filament:*FLEX08MK4*] -inherits = *FLEXMK4* -filament_max_volumetric_speed = 9 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 - [filament:ColorFabb bronzeFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -5035,6 +5135,15 @@ inherits = ColorFabb bronzeFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb bronzeFill @XL] +inherits = ColorFabb bronzeFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb bronzeFill @XL 0.6] +inherits = ColorFabb bronzeFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb bronzeFill @XL 0.8] +inherits = ColorFabb bronzeFill @PG 0.8; *PLA08XL* + [filament:ColorFabb steelFill] inherits = ColorFabb bronzeFill extrusion_multiplier = 1.15 @@ -5056,6 +5165,15 @@ inherits = ColorFabb steelFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb steelFill @XL] +inherits = ColorFabb steelFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb steelFill @XL 0.6] +inherits = ColorFabb steelFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb steelFill @XL 0.8] +inherits = ColorFabb steelFill @PG 0.8; *PLA08XL* + [filament:ColorFabb copperFill] inherits = ColorFabb bronzeFill extrusion_multiplier = 1.15 @@ -5075,6 +5193,15 @@ inherits = ColorFabb copperFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb copperFill @XL] +inherits = ColorFabb copperFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb copperFill @XL 0.6] +inherits = ColorFabb copperFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb copperFill @XL 0.8] +inherits = ColorFabb copperFill @PG 0.8; *PLA08XL* + [filament:ColorFabb HT] inherits = *PET* filament_vendor = ColorFabb @@ -5097,28 +5224,28 @@ temperature = 270 inherits = ColorFabb HT; *PETPG* first_layer_bed_temperature = 100 bed_temperature = 105 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:ColorFabb HT @PG 0.6] inherits = ColorFabb HT @PG; *PET06PG* -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:ColorFabb HT @PG 0.8] inherits = ColorFabb HT @PG; *PET08PG* +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 + +[filament:ColorFabb HT @XL] +inherits = ColorFabb HT @PG; *PETXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 + +[filament:ColorFabb HT @XL 0.6] +inherits = ColorFabb HT @PG 0.6; *PET06XL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 + +[filament:ColorFabb HT @XL 0.8] +inherits = ColorFabb HT @PG 0.8; *PET08XL* compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 -[filament:ColorFabb HT @MK4] -inherits = ColorFabb HT; *PETPG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 - -[filament:ColorFabb HT @MK4 0.6] -inherits = ColorFabb HT @MK4; *PET06PG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 - -[filament:ColorFabb HT @MK4 0.8] -inherits = ColorFabb HT @MK4; *PET08PG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 - [filament:ColorFabb PLA-PHA] inherits = *PLA* filament_vendor = ColorFabb @@ -5135,6 +5262,15 @@ inherits = ColorFabb PLA-PHA; *PLA06PG* [filament:ColorFabb PLA-PHA @PG 0.8] inherits = ColorFabb PLA-PHA; *PLA08PG* +[filament:ColorFabb PLA-PHA @XL] +inherits = ColorFabb PLA-PHA @PG; *PLAXL* + +[filament:ColorFabb PLA-PHA @XL 0.6] +inherits = ColorFabb PLA-PHA @PG 0.6; *PLA06XL* + +[filament:ColorFabb PLA-PHA @XL 0.8] +inherits = ColorFabb PLA-PHA @PG 0.8; *PLA08XL* + [filament:ColorFabb woodFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -5162,6 +5298,15 @@ inherits = ColorFabb woodFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1 +[filament:ColorFabb woodFill @XL] +inherits = ColorFabb woodFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb woodFill @XL 0.6] +inherits = ColorFabb woodFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb woodFill @XL 0.8] +inherits = ColorFabb woodFill @PG 0.8; *PLA08XL* + [filament:ColorFabb corkFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -5189,6 +5334,15 @@ inherits = ColorFabb corkFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb corkFill @XL] +inherits = ColorFabb corkFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb corkFill @XL 0.6] +inherits = ColorFabb corkFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb corkFill @XL 0.8] +inherits = ColorFabb corkFill @PG 0.8; *PLA08XL* + [filament:ColorFabb XT] inherits = *PET* filament_vendor = ColorFabb @@ -5208,6 +5362,15 @@ inherits = ColorFabb XT; *PET06PG* [filament:ColorFabb XT @PG 0.8] inherits = ColorFabb XT; *PET08PG* +[filament:ColorFabb XT @XL] +inherits = ColorFabb XT @PG; *PETXL* + +[filament:ColorFabb XT @XL 0.6] +inherits = ColorFabb XT @PG 0.6; *PET06XL* + +[filament:ColorFabb XT @XL 0.8] +inherits = ColorFabb XT @PG 0.8; *PET08XL* + [filament:ColorFabb XT-CF20] inherits = *PET* filament_vendor = ColorFabb @@ -5237,6 +5400,15 @@ filament_max_volumetric_speed = 6 inherits = ColorFabb XT-CF20 @PG; *PET08PG* filament_max_volumetric_speed = 9 +[filament:ColorFabb XT-CF20 @XL] +inherits = ColorFabb XT-CF20 @PG; *PETXL*; *04PLUSXL* + +[filament:ColorFabb XT-CF20 @XL 0.6] +inherits = ColorFabb XT-CF20 @PG 0.6; *PET06XL* + +[filament:ColorFabb XT-CF20 @XL 0.8] +inherits = ColorFabb XT-CF20 @PG 0.8; *PET08XL* + [filament:ColorFabb nGen] inherits = *PET* filament_vendor = ColorFabb @@ -5255,10 +5427,19 @@ min_fan_speed = 20 inherits = ColorFabb nGen; *PETPG* [filament:ColorFabb nGen @PG 0.6] -inherits = ColorFabb nGen; *PET06PG* +inherits = ColorFabb nGen @PG; *PET06PG* [filament:ColorFabb nGen @PG 0.8] -inherits = ColorFabb nGen; *PET08PG* +inherits = ColorFabb nGen @PG; *PET08PG* + +[filament:ColorFabb nGen @XL] +inherits = ColorFabb nGen @PG; *PETPG* + +[filament:ColorFabb nGen @XL 0.6] +inherits = ColorFabb nGen @PG 0.6; *PET06XL* + +[filament:ColorFabb nGen @XL 0.8] +inherits = ColorFabb nGen @PG 0.8; *PET08XL* [filament:ColorFabb nGen flex] inherits = *FLEX* @@ -5282,30 +5463,37 @@ filament_retract_length = nil filament_retract_lift = 0 compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) -[filament:ColorFabb nGen flex @PG] -inherits = ColorFabb nGen flex; *FLEXPG* +[filament:ColorFabb nGen flex @XL] +inherits = ColorFabb nGen flex; *FLEXXL* +renamed_from = "ColorFabb nGen flex @PG" filament_max_volumetric_speed = 6 filament_retract_length = 2.5 +filament_multitool_ramming_flow = 6 +filament_retract_length_toolchange = 2.5 -[filament:ColorFabb nGen flex @PG 0.6] -inherits = ColorFabb nGen flex; *FLEX06PG* +[filament:ColorFabb nGen flex @XL 0.6] +inherits = ColorFabb nGen flex; *FLEX06XL* +renamed_from = "ColorFabb nGen flex @PG 0.6" filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 -[filament:ColorFabb nGen flex @PG 0.8] -inherits = ColorFabb nGen flex; *FLEX08PG* +[filament:ColorFabb nGen flex @XL 0.8] +inherits = ColorFabb nGen flex; *FLEX08XL* +renamed_from = "ColorFabb nGen flex @PG 0.8" filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 [filament:ColorFabb nGen flex @MK4] -inherits = ColorFabb nGen flex; *FLEXMK4* +inherits = ColorFabb nGen flex; *FLEXPG* filament_max_volumetric_speed = 7 filament_retract_length = 2.5 [filament:ColorFabb nGen flex @MK4 0.6] -inherits = ColorFabb nGen flex; *FLEX06MK4* +inherits = ColorFabb nGen flex; *FLEX06PG* filament_max_volumetric_speed = 9 [filament:ColorFabb nGen flex @MK4 0.8] -inherits = ColorFabb nGen flex; *FLEX08MK4* +inherits = ColorFabb nGen flex; *FLEX08PG* filament_max_volumetric_speed = 12 [filament:Kimya PETG Carbon] @@ -5335,6 +5523,15 @@ filament_max_volumetric_speed = 9 inherits = Kimya PETG Carbon @PG; *PET08PG* filament_max_volumetric_speed = 14 +[filament:Kimya PETG Carbon @XL] +inherits = Kimya PETG Carbon @PG; *PETXL*; *04PLUSXL* + +[filament:Kimya PETG Carbon @XL 0.6] +inherits = Kimya PETG Carbon @PG 0.6; *PET06XL* + +[filament:Kimya PETG Carbon @XL 0.8] +inherits = Kimya PETG Carbon @PG 0.8; *PET08XL* + [filament:Kimya ABS Carbon] inherits = *ABSC* filament_vendor = Kimya @@ -5346,31 +5543,32 @@ first_layer_temperature = 260 temperature = 260 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Kimya ABS Carbon @PG] -inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG* +[filament:Kimya ABS Carbon @XL] +inherits = Kimya ABS Carbon; *ABSPG*; *ABSXL*; *04PLUSXL* +renamed_from = "Kimya ABS Carbon @PG" bed_temperature = 105 filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ -[filament:Kimya ABS Carbon @PG 0.6] -inherits = Kimya ABS Carbon @PG; *ABS06PG* +[filament:Kimya ABS Carbon @XL 0.6] +inherits = Kimya ABS Carbon @XL; *ABS06XL* +renamed_from = "Kimya ABS Carbon @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Kimya ABS Carbon @PG 0.8] -inherits = Kimya ABS Carbon @PG; *ABS08PG* +[filament:Kimya ABS Carbon @XL 0.8] +inherits = Kimya ABS Carbon @XL; *ABS08XL* +renamed_from = "Kimya ABS Carbon @PG 0.8" filament_max_volumetric_speed = 14 [filament:Kimya ABS Carbon @MK4] -inherits = Kimya ABS Carbon; *ABSMK4* +inherits = Kimya ABS Carbon; *ABSPG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ [filament:Kimya ABS Carbon @MK4 0.6] -inherits = Kimya ABS Carbon @MK4; *ABS06MK4* +inherits = Kimya ABS Carbon @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Kimya ABS Carbon @MK4 0.8] -inherits = Kimya ABS Carbon @MK4; *ABS08MK4* +inherits = Kimya ABS Carbon @MK4; *ABS08PG* filament_max_volumetric_speed = 14 [filament:Kimya ABS Kevlar] @@ -5378,29 +5576,30 @@ inherits = Kimya ABS Carbon filament_vendor = Kimya filament_density = 1.037 -[filament:Kimya ABS Kevlar @PG] -inherits = Kimya ABS Kevlar; *ABSPG*; *04PLUSPG* +[filament:Kimya ABS Kevlar @XL] +inherits = Kimya ABS Kevlar; *ABSPG*; *ABSXL*; *04PLUSXL* +renamed_from = "Kimya ABS Kevlar @PG" bed_temperature = 105 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ -[filament:Kimya ABS Kevlar @PG 0.6] -inherits = Kimya ABS Kevlar @PG; *ABS06PG* +[filament:Kimya ABS Kevlar @XL 0.6] +inherits = Kimya ABS Kevlar @XL; *ABS06XL* +renamed_from = "Kimya ABS Kevlar @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Kimya ABS Kevlar @PG 0.8] -inherits = Kimya ABS Kevlar @PG; *ABS08PG* +[filament:Kimya ABS Kevlar @XL 0.8] +inherits = Kimya ABS Kevlar @XL; *ABS08XL* +renamed_from = "Kimya ABS Kevlar @PG 0.8" filament_max_volumetric_speed = 14 [filament:Kimya ABS Kevlar @MK4] -inherits = Kimya ABS Kevlar; *ABSMK4*; *04PLUSPG* -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ +inherits = Kimya ABS Kevlar; *ABSPG*; *04PLUSPG* [filament:Kimya ABS Kevlar @MK4 0.6] -inherits = Kimya ABS Kevlar @MK4; *ABS06MK4* +inherits = Kimya ABS Kevlar @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Kimya ABS Kevlar @MK4 0.8] -inherits = Kimya ABS Kevlar @MK4; *ABS08MK4* +inherits = Kimya ABS Kevlar @MK4; *ABS08PG* filament_max_volumetric_speed = 14 [filament:Kimya PEBA-S] @@ -5429,6 +5628,15 @@ filament_max_volumetric_speed = 8 inherits = Kimya PEBA-S @PG; *PET08PG* filament_max_volumetric_speed = 10 +[filament:Kimya PEBA-S @XL] +inherits = Kimya PEBA-S @PG; *PETXL* + +[filament:Kimya PEBA-S @XL 0.6] +inherits = Kimya PEBA-S @PG 0.6; *PET06XL* + +[filament:Kimya PEBA-S @XL 0.8] +inherits = Kimya PEBA-S @PG 0.8; *PET08XL* + [filament:E3D Edge] inherits = *PET* filament_vendor = E3D @@ -5445,6 +5653,15 @@ inherits = E3D Edge; *PET06PG* [filament:E3D Edge @PG 0.8] inherits = E3D Edge; *PET08PG* +[filament:E3D Edge @XL] +inherits = E3D Edge @PG; *PETXL* + +[filament:E3D Edge @XL 0.6] +inherits = E3D Edge @PG 0.6; *PET06XL* + +[filament:E3D Edge @XL 0.8] +inherits = E3D Edge @PG 0.8; *PET08XL* + [filament:E3D PC-ABS] ## discontinued inherits = *ABS* @@ -5471,6 +5688,15 @@ inherits = Fillamentum PLA; *PLA06PG* [filament:Fillamentum PLA @PG 0.8] inherits = Fillamentum PLA; *PLA08PG* +[filament:Fillamentum PLA @XL] +inherits = Fillamentum PLA @PG; *PLAXL* + +[filament:Fillamentum PLA @XL 0.6] +inherits = Fillamentum PLA @PG 0.6; *PLA06XL* + +[filament:Fillamentum PLA @XL 0.8] +inherits = Fillamentum PLA @PG 0.8; *PLA08XL* + [filament:Fillamentum ABS] inherits = *ABSC* filament_vendor = Fillamentum @@ -5480,24 +5706,27 @@ filament_spool_weight = 230 first_layer_temperature = 240 temperature = 240 -[filament:Fillamentum ABS @PG] -inherits = Fillamentum ABS; *ABSPG* +[filament:Fillamentum ABS @XL] +inherits = Fillamentum ABS; *ABSPG*; *ABSXL* +renamed_from = "Fillamentum ABS @PG" bed_temperature = 105 -[filament:Fillamentum ABS @PG 0.6] -inherits = Fillamentum ABS @PG; *ABS06PG* +[filament:Fillamentum ABS @XL 0.6] +inherits = Fillamentum ABS @XL; *ABS06XL* +renamed_from = "Fillamentum ABS @PG 0.6" -[filament:Fillamentum ABS @PG 0.8] -inherits = Fillamentum ABS @PG; *ABS08PG* +[filament:Fillamentum ABS @XL 0.8] +inherits = Fillamentum ABS @XL; *ABS08XL* +renamed_from = "Fillamentum ABS @PG 0.8" [filament:Fillamentum ABS @MK4] -inherits = Fillamentum ABS; *ABSMK4* +inherits = Fillamentum ABS; *ABSPG* [filament:Fillamentum ABS @MK4 0.6] -inherits = Fillamentum ABS @MK4; *ABS06MK4* +inherits = Fillamentum ABS @MK4; *ABS06PG* [filament:Fillamentum ABS @MK4 0.8] -inherits = Fillamentum ABS @MK4; *ABS08MK4* +inherits = Fillamentum ABS @MK4; *ABS08PG* [filament:Fillamentum ASA] inherits = *ABS* @@ -5515,28 +5744,31 @@ first_layer_temperature = 260 temperature = 260 filament_type = ASA -[filament:Fillamentum ASA @PG] -inherits = Fillamentum ASA; *ABSPG* +[filament:Fillamentum ASA @XL] +inherits = Fillamentum ASA; *ABSPG*; *ABSXL* +renamed_from = "Fillamentum ASA @PG" bed_temperature = 105 min_fan_speed = 10 max_fan_speed = 10 -[filament:Fillamentum ASA @PG 0.6] -inherits = Fillamentum ASA @PG; *ABS06PG* +[filament:Fillamentum ASA @XL 0.6] +inherits = Fillamentum ASA @XL; *ABS06XL* +renamed_from = "Fillamentum ASA @PG 0.6" -[filament:Fillamentum ASA @PG 0.8] -inherits = Fillamentum ASA @PG; *ABS08PG* +[filament:Fillamentum ASA @XL 0.8] +inherits = Fillamentum ASA @XL; *ABS08XL* +renamed_from = "Fillamentum ASA @PG 0.8" [filament:Fillamentum ASA @MK4] -inherits = Fillamentum ASA; *ABSMK4* +inherits = Fillamentum ASA; *ABSPG* min_fan_speed = 10 max_fan_speed = 10 [filament:Fillamentum ASA @MK4 0.6] -inherits = Fillamentum ASA @MK4; *ABS06MK4* +inherits = Fillamentum ASA @MK4; *ABS06PG* [filament:Fillamentum ASA @MK4 0.8] -inherits = Fillamentum ASA @MK4; *ABS08MK4* +inherits = Fillamentum ASA @MK4; *ABS08PG* [filament:Prusament ASA] inherits = *ABS* @@ -5561,31 +5793,34 @@ filament_colour = #FFF2EC start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusament ASA @PG] -inherits = Prusament ASA; *ABSPG* +[filament:Prusament ASA @XL] +inherits = Prusament ASA; *ABSPG*; *ABSXL* +renamed_from = "Prusament ASA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 min_fan_speed = 10 max_fan_speed = 10 -[filament:Prusament ASA @PG 0.6] -inherits = Prusament ASA @PG; *ABS06PG* +[filament:Prusament ASA @XL 0.6] +inherits = Prusament ASA @XL; *ABS06XL* +renamed_from = "Prusament ASA @PG 0.6" -[filament:Prusament ASA @PG 0.8] -inherits = Prusament ASA @PG; *ABS08PG* +[filament:Prusament ASA @XL 0.8] +inherits = Prusament ASA @XL; *ABS08XL* +renamed_from = "Prusament ASA @PG 0.8" first_layer_temperature = 265 temperature = 265 [filament:Prusament ASA @MK4] -inherits = Prusament ASA; *ABSMK4* +inherits = Prusament ASA; *ABSPG* min_fan_speed = 10 max_fan_speed = 10 [filament:Prusament ASA @MK4 0.6] -inherits = Prusament ASA @MK4; *ABS06MK4* +inherits = Prusament ASA @MK4; *ABS06PG* [filament:Prusament ASA @MK4 0.8] -inherits = Prusament ASA @MK4; *ABS08MK4* +inherits = Prusament ASA @MK4; *ABS08PG* first_layer_temperature = 265 temperature = 265 @@ -5615,32 +5850,35 @@ filament_retract_lift = 0.2 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -[filament:Prusament PC Blend @PG] +[filament:Prusament PC Blend @XL] +inherits = Prusament PC Blend; *PCPG*; *PCXL* +renamed_from = "Prusament PC Blend @PG" +filament_max_volumetric_speed = 9 +min_fan_speed = 10 +max_fan_speed = 10 + +[filament:Prusament PC Blend @XL 0.6] +inherits = Prusament PC Blend @XL; *PC06XL* +renamed_from = "Prusament PC Blend @PG 0.6" +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend @XL 0.8] +inherits = Prusament PC Blend @XL; *PC08XL* +renamed_from = "Prusament PC Blend @PG 0.8" +filament_max_volumetric_speed = 18 + +[filament:Prusament PC Blend @MK4] inherits = Prusament PC Blend; *PCPG* filament_max_volumetric_speed = 9 min_fan_speed = 10 max_fan_speed = 10 -[filament:Prusament PC Blend @PG 0.6] -inherits = Prusament PC Blend @PG; *PC06PG* -filament_max_volumetric_speed = 13 - -[filament:Prusament PC Blend @PG 0.8] -inherits = Prusament PC Blend @PG; *PC08PG* -filament_max_volumetric_speed = 18 - -[filament:Prusament PC Blend @MK4] -inherits = Prusament PC Blend; *PCMK4* -filament_max_volumetric_speed = 9 -min_fan_speed = 10 -max_fan_speed = 10 - [filament:Prusament PC Blend @MK4 0.6] -inherits = Prusament PC Blend @MK4; *PC06MK4* +inherits = Prusament PC Blend @MK4; *PC06PG* filament_max_volumetric_speed = 13 [filament:Prusament PC Blend @MK4 0.8] -inherits = Prusament PC Blend @MK4; *PC08MK4* +inherits = Prusament PC Blend @MK4; *PC08PG* filament_max_volumetric_speed = 18 [filament:Prusament PC Blend @MK2] @@ -5664,30 +5902,33 @@ filament_retract_length = nil filament_retract_lift = nil compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusament PC Blend Carbon Fiber @PG] +[filament:Prusament PC Blend Carbon Fiber @XL] +inherits = Prusament PC Blend Carbon Fiber; *PCPG*; *PCXL* +renamed_from = "Prusament PC Blend Carbon Fiber @PG" +min_fan_speed = 10 +max_fan_speed = 10 + +[filament:Prusament PC Blend Carbon Fiber @XL 0.6] +inherits = Prusament PC Blend Carbon Fiber @XL; *PC06XL* +renamed_from = "Prusament PC Blend Carbon Fiber @PG 0.6" +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend Carbon Fiber @XL 0.8] +inherits = Prusament PC Blend Carbon Fiber @XL; *PC08XL* +renamed_from = "Prusament PC Blend Carbon Fiber @PG 0.8" +filament_max_volumetric_speed = 18 + +[filament:Prusament PC Blend Carbon Fiber @MK4] inherits = Prusament PC Blend Carbon Fiber; *PCPG* min_fan_speed = 10 max_fan_speed = 10 -[filament:Prusament PC Blend Carbon Fiber @PG 0.6] -inherits = Prusament PC Blend Carbon Fiber @PG; *PC06PG* -filament_max_volumetric_speed = 13 - -[filament:Prusament PC Blend Carbon Fiber @PG 0.8] -inherits = Prusament PC Blend Carbon Fiber @PG; *PC08PG* -filament_max_volumetric_speed = 18 - -[filament:Prusament PC Blend Carbon Fiber @MK4] -inherits = Prusament PC Blend Carbon Fiber; *PCMK4* -min_fan_speed = 10 -max_fan_speed = 10 - [filament:Prusament PC Blend Carbon Fiber @MK4 0.6] -inherits = Prusament PC Blend Carbon Fiber @MK4; *PC06MK4* +inherits = Prusament PC Blend Carbon Fiber @MK4; *PC06PG* filament_max_volumetric_speed = 13 [filament:Prusament PC Blend Carbon Fiber @MK4 0.8] -inherits = Prusament PC Blend Carbon Fiber @MK4; *PC08MK4* +inherits = Prusament PC Blend Carbon Fiber @MK4; *PC08PG* filament_max_volumetric_speed = 18 [filament:Prusament PC Blend Carbon Fiber @MK2] @@ -5712,28 +5953,28 @@ bed_temperature = 115 fan_below_layer_time = 10 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusament PA11 Carbon Fiber @PG] -inherits = Prusament PA11 Carbon Fiber; *PCPG* +[filament:Prusament PA11 Carbon Fiber @XL] +inherits = Prusament PA11 Carbon Fiber; *PCPG*; *PCXL* filament_max_volumetric_speed = 6.5 -[filament:Prusament PA11 Carbon Fiber @PG 0.6] -inherits = Prusament PA11 Carbon Fiber @PG; *PC06PG* +[filament:Prusament PA11 Carbon Fiber @XL 0.6] +inherits = Prusament PA11 Carbon Fiber @XL; *PC06XL* filament_max_volumetric_speed = 8 -[filament:Prusament PA11 Carbon Fiber @PG 0.8] -inherits = Prusament PA11 Carbon Fiber @PG; *PC08PG* +[filament:Prusament PA11 Carbon Fiber @XL 0.8] +inherits = Prusament PA11 Carbon Fiber @XL; *PC08XL* filament_max_volumetric_speed = 10 [filament:Prusament PA11 Carbon Fiber @MK4] -inherits = Prusament PA11 Carbon Fiber; *PCMK4* +inherits = Prusament PA11 Carbon Fiber; *PCPG* filament_max_volumetric_speed = 6.5 [filament:Prusament PA11 Carbon Fiber @MK4 0.6] -inherits = Prusament PA11 Carbon Fiber @MK4; *PC06MK4* +inherits = Prusament PA11 Carbon Fiber @MK4; *PC06PG* filament_max_volumetric_speed = 8 [filament:Prusament PA11 Carbon Fiber @MK4 0.8] -inherits = Prusament PA11 Carbon Fiber @MK4; *PC08MK4* +inherits = Prusament PA11 Carbon Fiber @MK4; *PC08PG* filament_max_volumetric_speed = 10 [filament:Prusament PA11 Carbon Fiber @MK2] @@ -5769,6 +6010,15 @@ inherits = Fillamentum CPE; *PET06PG* [filament:Fillamentum CPE @PG 0.8] inherits = Fillamentum CPE; *PET08PG* +[filament:Fillamentum CPE @XL] +inherits = Fillamentum CPE @PG; *PETXL* + +[filament:Fillamentum CPE @XL 0.6] +inherits = Fillamentum CPE @PG 0.6; *PET06XL* + +[filament:Fillamentum CPE @XL 0.8] +inherits = Fillamentum CPE @PG 0.8; *PET08XL* + [filament:Fillamentum Timberfill] inherits = *PLA*; *04PLUS* filament_vendor = Fillamentum @@ -5797,6 +6047,15 @@ inherits = Fillamentum Timberfill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:Fillamentum Timberfill @XL] +inherits = Fillamentum Timberfill @PG; *PLAXL*; *04PLUSXL* + +[filament:Fillamentum Timberfill @XL 0.6] +inherits = Fillamentum Timberfill @PG 0.6; *PLA06XL* + +[filament:Fillamentum Timberfill @XL 0.8] +inherits = Fillamentum Timberfill @PG 0.8; *PLA08XL* + [filament:Smartfil Wood] inherits = *PLA*; *04PLUS* filament_vendor = Smart Materials 3D @@ -5824,6 +6083,15 @@ inherits = Smartfil Wood; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:Smartfil Wood @XL] +inherits = Smartfil Wood @PG; *PLAXL* + +[filament:Smartfil Wood @XL 0.6] +inherits = Smartfil Wood @PG 0.6; *PLA06XL* + +[filament:Smartfil Wood @XL 0.8] +inherits = Smartfil Wood @PG 0.8; *PLA08XL* + [filament:Generic ABS] inherits = *ABSC* filament_vendor = Generic @@ -5831,26 +6099,29 @@ filament_cost = 27.82 filament_density = 1.04 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Generic ABS @PG] -inherits = Generic ABS; *ABSPG* +[filament:Generic ABS @XL] +inherits = Generic ABS; *ABSPG*; *ABSXL* +renamed_from = "Generic ABS @PG" bed_temperature = 105 -[filament:Generic ABS @PG 0.6] -inherits = Generic ABS @PG; *ABS06PG* +[filament:Generic ABS @XL 0.6] +inherits = Generic ABS @XL; *ABS06XL* +renamed_from = "Generic ABS @PG 0.6" -[filament:Generic ABS @PG 0.8] -inherits = Generic ABS @PG; *ABS08PG* +[filament:Generic ABS @XL 0.8] +inherits = Generic ABS @XL; *ABS08XL* +renamed_from = "Generic ABS @PG 0.8" first_layer_temperature = 265 temperature = 265 [filament:Generic ABS @MK4] -inherits = Generic ABS; *ABSMK4* +inherits = Generic ABS; *ABSPG* [filament:Generic ABS @MK4 0.6] -inherits = Generic ABS @MK4; *ABS06MK4* +inherits = Generic ABS @MK4; *ABS06PG* [filament:Generic ABS @MK4 0.8] -inherits = Generic ABS @MK4; *ABS08MK4* +inherits = Generic ABS @MK4; *ABS08PG* first_layer_temperature = 265 temperature = 265 @@ -5861,24 +6132,27 @@ filament_cost = 27.82 filament_density = 1.01 filament_spool_weight = 265 -[filament:Esun ABS @PG] -inherits = Esun ABS; *ABSPG* +[filament:Esun ABS @XL] +inherits = Esun ABS; *ABSPG*; *ABSXL* +renamed_from = "Esun ABS @PG" bed_temperature = 105 -[filament:Esun ABS @PG 0.6] -inherits = Esun ABS @PG; *ABS06PG* +[filament:Esun ABS @XL 0.6] +inherits = Esun ABS @XL; *ABS06XL* +renamed_from = "Esun ABS @PG 0.6" -[filament:Esun ABS @PG 0.8] -inherits = Esun ABS @PG; *ABS08PG* +[filament:Esun ABS @XL 0.8] +inherits = Esun ABS @XL; *ABS08XL* +renamed_from = "Esun ABS @PG 0.8" [filament:Esun ABS @MK4] -inherits = Esun ABS; *ABSMK4* +inherits = Esun ABS; *ABSPG* [filament:Esun ABS @MK4 0.6] -inherits = Esun ABS @MK4; *ABS06MK4* +inherits = Esun ABS @MK4; *ABS06PG* [filament:Esun ABS @MK4 0.8] -inherits = Esun ABS @MK4; *ABS08MK4* +inherits = Esun ABS @MK4; *ABS08PG* [filament:Hatchbox ABS] inherits = *ABSC* @@ -5887,24 +6161,27 @@ filament_cost = 27.82 filament_density = 1.04 filament_spool_weight = 245 -[filament:Hatchbox ABS @PG] -inherits = Hatchbox ABS; *ABSPG* +[filament:Hatchbox ABS @XL] +inherits = Hatchbox ABS; *ABSPG*; *ABSXL* +renamed_from = "Hatchbox ABS @PG" bed_temperature = 105 -[filament:Hatchbox ABS @PG 0.6] -inherits = Hatchbox ABS @PG; *ABS06PG* +[filament:Hatchbox ABS @XL 0.6] +inherits = Hatchbox ABS @XL; *ABS06XL* +renamed_from = "Hatchbox ABS @PG 0.6" -[filament:Hatchbox ABS @PG 0.8] -inherits = Hatchbox ABS @PG; *ABS08PG* +[filament:Hatchbox ABS @XL 0.8] +inherits = Hatchbox ABS @XL; *ABS08XL* +renamed_from = "Hatchbox ABS @PG 0.8" [filament:Hatchbox ABS @MK4] -inherits = Hatchbox ABS; *ABSMK4* +inherits = Hatchbox ABS; *ABSPG* [filament:Hatchbox ABS @MK4 0.6] -inherits = Hatchbox ABS @MK4; *ABS06MK4* +inherits = Hatchbox ABS @MK4; *ABS06PG* [filament:Hatchbox ABS @MK4 0.8] -inherits = Hatchbox ABS @MK4; *ABS08MK4* +inherits = Hatchbox ABS @MK4; *ABS08PG* [filament:Filament PM ABS] inherits = *ABSC* @@ -5914,24 +6191,27 @@ filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 -[filament:Filament PM ABS @PG] -inherits = Filament PM ABS; *ABSPG* +[filament:Filament PM ABS @XL] +inherits = Filament PM ABS; *ABSPG*; *ABSXL* +renamed_from = "Filament PM ABS @PG" bed_temperature = 105 -[filament:Filament PM ABS @PG 0.6] -inherits = Filament PM ABS @PG; *ABS06PG* +[filament:Filament PM ABS @XL 0.6] +inherits = Filament PM ABS @XL; *ABS06XL* +renamed_from = "Filament PM ABS @PG 0.6" -[filament:Filament PM ABS @PG 0.8] -inherits = Filament PM ABS @PG; *ABS08PG* +[filament:Filament PM ABS @XL 0.8] +inherits = Filament PM ABS @XL; *ABS08XL* +renamed_from = "Filament PM ABS @PG 0.8" [filament:Filament PM ABS @MK4] -inherits = Filament PM ABS; *ABSMK4* +inherits = Filament PM ABS; *ABSPG* [filament:Filament PM ABS @MK4 0.6] -inherits = Filament PM ABS @MK4; *ABS06MK4* +inherits = Filament PM ABS @MK4; *ABS06PG* [filament:Filament PM ABS @MK4 0.8] -inherits = Filament PM ABS @MK4; *ABS08MK4* +inherits = Filament PM ABS @MK4; *ABS08PG* [filament:Verbatim ABS] inherits = *ABSC* @@ -5941,24 +6221,27 @@ filament_density = 1.05 filament_spool_weight = 235 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -[filament:Verbatim ABS @PG] -inherits = Verbatim ABS; *ABSPG* +[filament:Verbatim ABS @XL] +inherits = Verbatim ABS; *ABSPG*; *ABSXL* +renamed_from = "Verbatim ABS @PG" bed_temperature = 105 -[filament:Verbatim ABS @PG 0.6] -inherits = Verbatim ABS @PG; *ABS06PG* +[filament:Verbatim ABS @XL 0.6] +inherits = Verbatim ABS @XL; *ABS06XL* +renamed_from = "Verbatim ABS @PG 0.6" -[filament:Verbatim ABS @PG 0.8] -inherits = Verbatim ABS @PG; *ABS08PG* +[filament:Verbatim ABS @XL 0.8] +inherits = Verbatim ABS @XL; *ABS08XL* +renamed_from = "Verbatim ABS @PG 0.8" [filament:Verbatim ABS @MK4] -inherits = Verbatim ABS; *ABSMK4* +inherits = Verbatim ABS; *ABSPG* [filament:Verbatim ABS @MK4 0.6] -inherits = Verbatim ABS @MK4; *ABS06MK4* +inherits = Verbatim ABS @MK4; *ABS06PG* [filament:Verbatim ABS @MK4 0.8] -inherits = Verbatim ABS @MK4; *ABS08MK4* +inherits = Verbatim ABS @MK4; *ABS08PG* [filament:Generic PETG] inherits = *PET* @@ -5970,18 +6253,27 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2S [filament:Generic PETG @PG] inherits = Generic PETG; *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PETG @PG 0.6] inherits = Generic PETG; *PET06PG* filament_max_volumetric_speed = 17 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Generic PETG @PG 0.8] inherits = Generic PETG; *PET08PG* first_layer_temperature = 240 temperature = 250 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Generic PETG @XL] +inherits = Generic PETG @PG; *PETXL* + +[filament:Generic PETG @XL 0.6] +inherits = Generic PETG @PG 0.6; *PET06XL* + +[filament:Generic PETG @XL 0.8] +inherits = Generic PETG @PG 0.8; *PET08XL* [filament:Generic PETG @PGIS] inherits = Generic PETG @PG @@ -5995,7 +6287,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PETG @PGIS 0.6] inherits = Generic PETG @PG 0.6 @@ -6005,7 +6297,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Generic PETG @PGIS 0.8] inherits = Generic PETG @PG 0.8 @@ -6015,7 +6307,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Extrudr DuraPro ASA] inherits = Fillamentum ASA @@ -6033,15 +6325,21 @@ filament_spool_weight = 230 [filament:Extrudr DuraPro ASA @PG] inherits = Extrudr DuraPro ASA; *ABSPG* filament_max_volumetric_speed = 10 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr DuraPro ASA @PG 0.6] inherits = Extrudr DuraPro ASA @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr DuraPro ASA @PG 0.8] inherits = Extrudr DuraPro ASA @PG; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Extrudr DuraPro ASA @XL] +inherits = Extrudr DuraPro ASA @PG; *ABSXL* + +[filament:Extrudr DuraPro ASA @XL 0.6] +inherits = Extrudr DuraPro ASA @PG 0.6; *ABS06XL* + +[filament:Extrudr DuraPro ASA @XL 0.8] +inherits = Extrudr DuraPro ASA @PG 0.8; *ABS08XL* [filament:Extrudr PETG] inherits = *PET* @@ -6068,6 +6366,15 @@ inherits = Extrudr PETG; *PET06PG* [filament:Extrudr PETG @PG 0.8] inherits = Extrudr PETG; *PET08PG* +[filament:Extrudr PETG @XL] +inherits = Extrudr PETG @PG; *PETXL* + +[filament:Extrudr PETG @XL 0.6] +inherits = Extrudr PETG @PG 0.6; *PET06XL* + +[filament:Extrudr PETG @XL 0.8] +inherits = Extrudr PETG @PG 0.8; *PET08XL* + [filament:Extrudr PETG @MINI] inherits = Extrudr PETG; *PETMINI* filament_vendor = Extrudr @@ -6091,6 +6398,15 @@ inherits = Extrudr XPETG CF; *PET06PG* [filament:Extrudr XPETG CF @PG 0.8] inherits = Extrudr XPETG CF; *PET08PG* +[filament:Extrudr XPETG CF @XL] +inherits = Extrudr XPETG CF @PG; *PETXL* + +[filament:Extrudr XPETG CF @XL 0.6] +inherits = Extrudr XPETG CF @PG 0.6; *PET06XL* + +[filament:Extrudr XPETG CF @XL 0.8] +inherits = Extrudr XPETG CF @PG 0.8; *PET08XL* + [filament:Extrudr XPETG CF @MINI] inherits = Extrudr XPETG CF; *PETMINI* compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" @@ -6112,6 +6428,15 @@ inherits = Extrudr XPETG Matt; *PET06PG* [filament:Extrudr XPETG Matt @PG 0.8] inherits = Extrudr XPETG Matt; *PET08PG* +[filament:Extrudr XPETG Matt @XL] +inherits = Extrudr XPETG Matt @PG; *PETXL* + +[filament:Extrudr XPETG Matt @XL 0.6] +inherits = Extrudr XPETG Matt @PG 0.6; *PET06XL* + +[filament:Extrudr XPETG Matt @XL 0.8] +inherits = Extrudr XPETG Matt @PG 0.8; *PET08XL* + [filament:Extrudr XPETG Matt @MINI] inherits = Extrudr XPETG Matt; *PETMINI* @@ -6140,6 +6465,15 @@ inherits = Extrudr BioFusion; *PLA06PG* [filament:Extrudr BioFusion @PG 0.8] inherits = Extrudr BioFusion; *PLA08PG* +[filament:Extrudr BioFusion @XL] +inherits = Extrudr BioFusion @PG; *PLAXL* + +[filament:Extrudr BioFusion @XL 0.6] +inherits = Extrudr BioFusion @PG 0.6; *PLA06XL* + +[filament:Extrudr BioFusion @XL 0.8] +inherits = Extrudr BioFusion @PG 0.8; *PLA08XL* + [filament:Extrudr Flax] inherits = *PLA* filament_vendor = Extrudr @@ -6169,6 +6503,15 @@ filament_max_volumetric_speed = 17 first_layer_temperature = 200 temperature = 200 +[filament:Extrudr Flax @XL] +inherits = Extrudr Flax @PG; *PLAXL* + +[filament:Extrudr Flax @XL 0.6] +inherits = Extrudr Flax @PG 0.6; *PLA06XL* + +[filament:Extrudr Flax @XL 0.8] +inherits = Extrudr Flax @PG 0.8; *PLA08XL* + [filament:Extrudr GreenTEC] inherits = *PLA* filament_vendor = Extrudr @@ -6189,6 +6532,15 @@ inherits = Extrudr GreenTEC; *PLA06PG* [filament:Extrudr GreenTEC @PG 0.8] inherits = Extrudr GreenTEC; *PLA08PG* +[filament:Extrudr GreenTEC @XL] +inherits = Extrudr GreenTEC @PG; *PLAXL* + +[filament:Extrudr GreenTEC @XL 0.6] +inherits = Extrudr GreenTEC @PG; *PLA06XL* + +[filament:Extrudr GreenTEC @XL 0.8] +inherits = Extrudr GreenTEC @PG; *PLA08XL* + [filament:Extrudr GreenTEC Pro] inherits = *PLA* filament_vendor = Extrudr @@ -6211,6 +6563,15 @@ inherits = Extrudr GreenTEC Pro; *PLA06PG* [filament:Extrudr GreenTEC Pro @PG 0.8] inherits = Extrudr GreenTEC Pro; *PLA08PG* +[filament:Extrudr GreenTEC Pro @XL] +inherits = Extrudr GreenTEC Pro @PG; *PLAXL* + +[filament:Extrudr GreenTEC Pro @XL 0.6] +inherits = Extrudr GreenTEC Pro @PG 0.6; *PLA06XL* + +[filament:Extrudr GreenTEC Pro @XL 0.8] +inherits = Extrudr GreenTEC Pro @PG 0.8; *PLA08XL* + [filament:Extrudr GreenTEC Pro Carbon] inherits = *PLA*; *04PLUS* filament_vendor = Extrudr @@ -6234,6 +6595,15 @@ inherits = Extrudr GreenTEC Pro Carbon; *PLA06PG* [filament:Extrudr GreenTEC Pro Carbon @PG 0.8] inherits = Extrudr GreenTEC Pro Carbon; *PLA08PG* +[filament:Extrudr GreenTEC Pro Carbon @XL] +inherits = Extrudr GreenTEC Pro Carbon @PG; *PLAXL* + +[filament:Extrudr GreenTEC Pro Carbon @XL 0.6] +inherits = Extrudr GreenTEC Pro Carbon @PG 0.6; *PLA06XL* + +[filament:Extrudr GreenTEC Pro Carbon @XL 0.8] +inherits = Extrudr GreenTEC Pro Carbon @PG 0.8; *PLA08XL* + [filament:Extrudr PLA NX1] inherits = *PLA* filament_vendor = Extrudr @@ -6259,6 +6629,15 @@ inherits = Extrudr PLA NX1; *PLA06PG* [filament:Extrudr PLA NX1 @PG 0.8] inherits = Extrudr PLA NX1; *PLA08PG* +[filament:Extrudr PLA NX1 @XL] +inherits = Extrudr PLA NX1 @PG; *PLAXL* + +[filament:Extrudr PLA NX1 @XL 0.6] +inherits = Extrudr PLA NX1 @PG; *PLA06XL* + +[filament:Extrudr PLA NX1 @XL 0.8] +inherits = Extrudr PLA NX1 @PG; *PLA08XL* + [filament:Extrudr PLA NX2] inherits = Extrudr PLA NX1 filament_cost = 23.63 @@ -6274,6 +6653,15 @@ inherits = Extrudr PLA NX2; *PLA06PG* [filament:Extrudr PLA NX2 @PG 0.8] inherits = Extrudr PLA NX2; *PLA08PG* +[filament:Extrudr PLA NX2 @XL] +inherits = Extrudr PLA NX2 @PG; *PLAXL* + +[filament:Extrudr PLA NX2 @XL 0.6] +inherits = Extrudr PLA NX2 @PG 0.6; *PLA06XL* + +[filament:Extrudr PLA NX2 @XL 0.8] +inherits = Extrudr PLA NX2 @PG 0.8; *PLA08XL* + [filament:Extrudr Flex Hard] inherits = *FLEX* filament_vendor = Extrudr @@ -6289,27 +6677,31 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 -[filament:Extrudr Flex Hard @PG] +[filament:Extrudr Flex Hard @XL] +inherits = Extrudr Flex Hard; *FLEXXL* +renamed_from = "Extrudr Flex Hard @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 +filament_retract_length_toolchange = 2.5 + +[filament:Extrudr Flex Hard @XL 0.6] +inherits = Extrudr Flex Hard @XL; *FLEX06XL* +renamed_from = "Extrudr Flex Hard @PG 0.6" + +[filament:Extrudr Flex Hard @XL 0.8] +inherits = Extrudr Flex Hard @XL; *FLEX08XL* +renamed_from = "Extrudr Flex Hard @PG 0.8" + +[filament:Extrudr Flex Hard @MK4] inherits = Extrudr Flex Hard; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 2.5 -[filament:Extrudr Flex Hard @PG 0.6] -inherits = Extrudr Flex Hard @PG; *FLEX06PG* - -[filament:Extrudr Flex Hard @PG 0.8] -inherits = Extrudr Flex Hard @PG; *FLEX08PG* - -[filament:Extrudr Flex Hard @MK4] -inherits = Extrudr Flex Hard; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 2.5 - [filament:Extrudr Flex Hard @MK4 0.6] -inherits = Extrudr Flex Hard @MK4; *FLEX06MK4* +inherits = Extrudr Flex Hard @MK4; *FLEX06PG* [filament:Extrudr Flex Hard @MK4 0.8] -inherits = Extrudr Flex Hard @MK4; *FLEX08MK4* +inherits = Extrudr Flex Hard @MK4; *FLEX08PG* [filament:Extrudr Flex Medium] inherits = *FLEX* @@ -6326,27 +6718,31 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 -[filament:Extrudr Flex Medium @PG] +[filament:Extrudr Flex Medium @XL] +inherits = Extrudr Flex Medium; *FLEXXL* +renamed_from = "Extrudr Flex Medium @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 +filament_retract_length_toolchange = 2.5 + +[filament:Extrudr Flex Medium @XL 0.6] +inherits = Extrudr Flex Medium @XL; *FLEX06XL* +renamed_from = "Extrudr Flex Medium @PG 0.6" + +[filament:Extrudr Flex Medium @XL 0.8] +inherits = Extrudr Flex Medium @XL; *FLEX08XL* +renamed_from = "Extrudr Flex Medium @PG 0.8" + +[filament:Extrudr Flex Medium @MK4] inherits = Extrudr Flex Medium; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 2.5 -[filament:Extrudr Flex Medium @PG 0.6] -inherits = Extrudr Flex Medium @PG; *FLEX06PG* - -[filament:Extrudr Flex Medium @PG 0.8] -inherits = Extrudr Flex Medium @PG; *FLEX08PG* - -[filament:Extrudr Flex Medium @MK4] -inherits = Extrudr Flex Medium; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 2.5 - [filament:Extrudr Flex Medium @MK4 0.6] -inherits = Extrudr Flex Medium @MK4; *FLEX06MK4* +inherits = Extrudr Flex Medium @MK4; *FLEX06PG* [filament:Extrudr Flex Medium @MK4 0.8] -inherits = Extrudr Flex Medium @MK4; *FLEX08MK4* +inherits = Extrudr Flex Medium @MK4; *FLEX08PG* [filament:Extrudr Flex SemiSoft] inherits = *FLEX* @@ -6363,32 +6759,37 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 -[filament:Extrudr Flex SemiSoft @PG] +[filament:Extrudr Flex SemiSoft @XL] +inherits = Extrudr Flex SemiSoft; *FLEXXL* +renamed_from = "Extrudr Flex SemiSoft @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 3 +filament_max_volumetric_speed = 3 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3 + +[filament:Extrudr Flex SemiSoft @XL 0.6] +inherits = Extrudr Flex SemiSoft @XL; *FLEX06XL* +renamed_from = "Extrudr Flex SemiSoft @PG 0.6" + +[filament:Extrudr Flex SemiSoft @XL 0.8] +inherits = Extrudr Flex SemiSoft @XL; *FLEX08XL* +renamed_from = "Extrudr Flex SemiSoft @PG 0.8" +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:Extrudr Flex SemiSoft @MK4] inherits = Extrudr Flex SemiSoft; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 3 filament_max_volumetric_speed = 3 -[filament:Extrudr Flex SemiSoft @PG 0.6] -inherits = Extrudr Flex SemiSoft @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:Extrudr Flex SemiSoft @PG 0.8] -inherits = Extrudr Flex SemiSoft @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:Extrudr Flex SemiSoft @MK4] -inherits = Extrudr Flex SemiSoft; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 3 -filament_max_volumetric_speed = 3 - [filament:Extrudr Flex SemiSoft @MK4 0.6] -inherits = Extrudr Flex SemiSoft @MK4; *FLEX06MK4* +inherits = Extrudr Flex SemiSoft @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Extrudr Flex SemiSoft @MK4 0.8] -inherits = Extrudr Flex SemiSoft @MK4; *FLEX08MK4* +inherits = Extrudr Flex SemiSoft @MK4; *FLEX08PG* filament_max_volumetric_speed = 8 [filament:addnorth Adamant S1] @@ -6418,34 +6819,41 @@ filament_spool_weight = 0 filament_retract_restart_extra = 0.1 filament_wipe = nil -[filament:addnorth Adamant S1 @PG] +[filament:addnorth Adamant S1 @XL] +inherits = addnorth Adamant S1; *FLEXXL* +renamed_from = "addnorth Adamant S1 @PG" +filament_max_volumetric_speed = 3 +filament_retract_length = 1.5 +filament_retract_restart_extra = 0 +filament_retract_lift = 0.2 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 1.5 + +[filament:addnorth Adamant S1 @XL 0.6] +inherits = addnorth Adamant S1 @XL; *FLEX06XL* +renamed_from = "addnorth Adamant S1 @PG 0.6" +filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 + +[filament:addnorth Adamant S1 @XL 0.8] +inherits = addnorth Adamant S1 @XL; *FLEX08XL* +renamed_from = "addnorth Adamant S1 @PG 0.8" +filament_max_volumetric_speed = 9 +filament_multitool_ramming_flow = 9 + +[filament:addnorth Adamant S1 @MK4] inherits = addnorth Adamant S1; *FLEXPG* filament_max_volumetric_speed = 3 filament_retract_length = 1.5 filament_retract_restart_extra = 0 filament_retract_lift = 0.2 -[filament:addnorth Adamant S1 @PG 0.6] -inherits = addnorth Adamant S1 @PG; *FLEX06PG* -filament_max_volumetric_speed = 4.5 - -[filament:addnorth Adamant S1 @PG 0.8] -inherits = addnorth Adamant S1 @PG; *FLEX08PG* -filament_max_volumetric_speed = 9 - -[filament:addnorth Adamant S1 @MK4] -inherits = addnorth Adamant S1; *FLEXMK4* -filament_max_volumetric_speed = 3 -filament_retract_length = 1.5 -filament_retract_restart_extra = 0 -filament_retract_lift = 0.2 - [filament:addnorth Adamant S1 @MK4 0.6] -inherits = addnorth Adamant S1 @MK4; *FLEX06MK4* +inherits = addnorth Adamant S1 @MK4; *FLEX06PG* filament_max_volumetric_speed = 5.5 [filament:addnorth Adamant S1 @MK4 0.8] -inherits = addnorth Adamant S1 @MK4; *FLEX08MK4* +inherits = addnorth Adamant S1 @MK4; *FLEX08PG* filament_max_volumetric_speed = 9 [filament:addnorth Adura X] @@ -6475,37 +6883,34 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no filament_spool_weight = 0 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:addnorth Adura X @PG] -inherits = addnorth Adura X; *PETPG* +[filament:addnorth Adura X @XL] +inherits = addnorth Adura X; *PETPG*; *PETXL* +renamed_from = "addnorth Adura X @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material -[filament:addnorth Adura X @PG 0.6] -inherits = addnorth Adura X @PG; *PET06PG* +[filament:addnorth Adura X @XL 0.6] +inherits = addnorth Adura X @XL; *PET06XL* +renamed_from = "addnorth Adura X @PG 0.6" filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material -[filament:addnorth Adura X @PG 0.8] -inherits = addnorth Adura X @PG; *PET08PG* +[filament:addnorth Adura X @XL 0.8] +inherits = addnorth Adura X @XL; *PET08XL* +renamed_from = "addnorth Adura X @PG 0.8" filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material [filament:addnorth Adura X @MK4] inherits = addnorth Adura X; *PETPG* filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:addnorth Adura X @MK4 0.6] inherits = addnorth Adura X @MK4; *PET06PG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:addnorth Adura X @MK4 0.8] inherits = addnorth Adura X @MK4; *PET08PG* filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:addnorth Adura X @MINI] inherits = addnorth Adura X @@ -6554,6 +6959,15 @@ inherits = addnorth E-PLA; *PLA06PG* [filament:addnorth E-PLA @PG 0.8] inherits = addnorth E-PLA; *PLA08PG* +[filament:addnorth E-PLA @XL] +inherits = addnorth E-PLA @PG; *PLAXL* + +[filament:addnorth E-PLA @XL 0.6] +inherits = addnorth E-PLA @PG 0.6; *PLA06XL* + +[filament:addnorth E-PLA @XL 0.8] +inherits = addnorth E-PLA @PG 0.8; *PLA08XL* + [filament:addnorth ESD-PETG] inherits = *PET* filament_vendor = addnorth @@ -6590,6 +7004,15 @@ filament_max_volumetric_speed = 3.5 inherits = addnorth ESD-PETG @PG; *PET08PG* filament_max_volumetric_speed = 6 +[filament:addnorth ESD-PETG @XL] +inherits = addnorth ESD-PETG @PG; *PETXL* + +[filament:addnorth ESD-PETG @XL 0.6] +inherits = addnorth ESD-PETG @PG 0.6; *PET06XL* + +[filament:addnorth ESD-PETG @XL 0.8] +inherits = addnorth ESD-PETG @PG 0.8; *PET08XL* + [filament:addnorth ESD-PETG @MINI] inherits = addnorth ESD-PETG filament_retract_length = nil @@ -6631,30 +7054,37 @@ filament_deretract_speed = 25 filament_spool_weight = 0 filament_notes = "Use Magigoo PP bed adhesive or PP packing tape (on a cold printbed)." -[filament:addnorth OBC Polyethylene @PG] +[filament:addnorth OBC Polyethylene @XL] +inherits = addnorth OBC Polyethylene; *FLEXXL* +renamed_from = "addnorth OBC Polyethylene @PG" +filament_max_volumetric_speed = 4 +filament_retract_length = 1.5 +filament_multitool_ramming_flow = 4 +filament_retract_length_toolchange = 1.5 + +[filament:addnorth OBC Polyethylene @XL 0.6] +inherits = addnorth OBC Polyethylene @XL; *FLEX06XL* +renamed_from = "addnorth OBC Polyethylene @PG 0.6" +filament_max_volumetric_speed = 6 +filament_multitool_ramming_flow = 6 + +[filament:addnorth OBC Polyethylene @XL 0.8] +inherits = addnorth OBC Polyethylene @XL; *FLEX08XL* +renamed_from = "addnorth OBC Polyethylene @PG 0.8" +filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 + +[filament:addnorth OBC Polyethylene @MK4] inherits = addnorth OBC Polyethylene; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 1.5 -[filament:addnorth OBC Polyethylene @PG 0.6] -inherits = addnorth OBC Polyethylene @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 - -[filament:addnorth OBC Polyethylene @PG 0.8] -inherits = addnorth OBC Polyethylene @PG; *FLEX08PG* -filament_max_volumetric_speed = 10 - -[filament:addnorth OBC Polyethylene @MK4] -inherits = addnorth OBC Polyethylene; *FLEXMK4* -filament_max_volumetric_speed = 4 -filament_retract_length = 1.5 - [filament:addnorth OBC Polyethylene @MK4 0.6] -inherits = addnorth OBC Polyethylene @MK4; *FLEX06MK4* +inherits = addnorth OBC Polyethylene @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:addnorth OBC Polyethylene @MK4 0.8] -inherits = addnorth OBC Polyethylene @MK4; *FLEX08MK4* +inherits = addnorth OBC Polyethylene @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:addnorth PETG] @@ -6688,6 +7118,15 @@ inherits = addnorth PETG @PG; *PET06PG* [filament:addnorth PETG @PG 0.8] inherits = addnorth PETG @PG; *PET08PG* +[filament:addnorth PETG @XL] +inherits = addnorth PETG @PG; *PETXL* + +[filament:addnorth PETG @XL 0.6] +inherits = addnorth PETG @PG 0.6; *PET06XL* + +[filament:addnorth PETG @XL 0.8] +inherits = addnorth PETG @PG 0.8; *PET08XL* + [filament:addnorth PETG @MINI] inherits = addnorth PETG filament_retract_length = nil @@ -6741,6 +7180,15 @@ filament_max_volumetric_speed = 7 inherits = addnorth Rigid X @PG; *PET08PG* filament_max_volumetric_speed = 10 +[filament:addnorth Rigid X @XL] +inherits = addnorth Rigid X @PG; *PETXL*; *04PLUSXL* + +[filament:addnorth Rigid X @XL 0.6] +inherits = addnorth Rigid X @PG 0.6; *PET06XL* + +[filament:addnorth Rigid X @XL 0.8] +inherits = addnorth Rigid X @PG 0.8; *PET08XL* + [filament:addnorth Rigid X @MINI] inherits = addnorth Rigid X filament_retract_length = nil @@ -6784,6 +7232,15 @@ inherits = addnorth Textura; *PLA06PG* [filament:addnorth Textura @PG 0.8] inherits = addnorth Textura; *PLA08PG* +[filament:addnorth Textura @XL] +inherits = addnorth Textura @PG; *PLAXL* + +[filament:addnorth Textura @XL 0.6] +inherits = addnorth Textura @PG 0.6; *PLA06XL* + +[filament:addnorth Textura @XL 0.8] +inherits = addnorth Textura @PG 0.8; *PLA08XL* + [filament:addnorth Textura @MINI] inherits = addnorth Textura filament_retract_length = nil @@ -6815,17 +7272,23 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI [filament:Filamentworld ABS @PG] inherits = Filamentworld ABS; *ABSPG* first_layer_bed_temperature = 100 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG 0.6] inherits = Filamentworld ABS @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG 0.8] inherits = Filamentworld ABS @PG; *ABS08PG* first_layer_temperature = 240 temperature = 240 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Filamentworld ABS @XL] +inherits = Filamentworld ABS @PG; *ABSXL* + +[filament:Filamentworld ABS @XL 0.6] +inherits = Filamentworld ABS @PG 0.6; *ABS06XL* + +[filament:Filamentworld ABS @XL 0.8] +inherits = Filamentworld ABS @PG 0.8; *ABS08XL* [filament:Filamentworld ABS @MINI] inherits = Filamentworld ABS @@ -6868,6 +7331,15 @@ inherits = Filamentworld PETG @PG; *PET08PG* first_layer_temperature = 240 temperature = 245 +[filament:Filamentworld PETG @XL] +inherits = Filamentworld PETG @PG; *PETXL* + +[filament:Filamentworld PETG @XL 0.6] +inherits = Filamentworld PETG @PG 0.6; *PET06XL* + +[filament:Filamentworld PETG @XL 0.8] +inherits = Filamentworld PETG @PG 0.8; *PET08XL* + [filament:Filamentworld PETG @MINI] inherits = Filamentworld PETG filament_retract_length = nil @@ -6900,6 +7372,15 @@ inherits = Filamentworld PLA; *PLA06PG* [filament:Filamentworld PLA @PG 0.8] inherits = Filamentworld PLA; *PLA08PG* +[filament:Filamentworld PLA @XL] +inherits = Filamentworld PLA @PG; *PLAXL* + +[filament:Filamentworld PLA @XL 0.6] +inherits = Filamentworld PLA @PG 0.6; *PLA06XL* + +[filament:Filamentworld PLA @XL 0.8] +inherits = Filamentworld PLA @PG 0.8; *PLA08XL* + [filament:Filament PM PETG] inherits = *PET* renamed_from = "Plasty Mladec PETG" @@ -6918,6 +7399,15 @@ inherits = Filament PM PETG; *PET06PG* [filament:Filament PM PETG @PG 0.8] inherits = Filament PM PETG; *PET08PG* +[filament:Filament PM PETG @XL] +inherits = Filament PM PETG @PG; *PETXL* + +[filament:Filament PM PETG @XL 0.6] +inherits = Filament PM PETG @PG 0.6; *PET06XL* + +[filament:Filament PM PETG @XL 0.8] +inherits = Filament PM PETG @PG 0.8; *PET08XL* + [filament:Generic PLA] inherits = *PLA* filament_vendor = Generic @@ -6927,18 +7417,27 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG [filament:Generic PLA @PG] inherits = Generic PLA; *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PLA @PG 0.6] inherits = Generic PLA; *PLA06PG* filament_max_volumetric_speed = 15 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Generic PLA @PG 0.8] inherits = Generic PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Generic PLA @XL] +inherits = Generic PLA @PG; *PLAXL* + +[filament:Generic PLA @XL 0.6] +inherits = Generic PLA @PG 0.6; *PLA06XL* + +[filament:Generic PLA @XL 0.8] +inherits = Generic PLA @PG 0.8; *PLA08XL* [filament:Generic PLA @PGIS] inherits = Generic PLA @PG @@ -6946,19 +7445,19 @@ renamed_from = "Generic PLA @MK4IS" first_layer_temperature = 230 temperature = 220 slowdown_below_layer_time = 6 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PLA @PGIS 0.6] inherits = Generic PLA @PG 0.6 first_layer_temperature = 230 temperature = 220 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Generic PLA @PGIS 0.8] inherits = Generic PLA @PG 0.8 first_layer_temperature = 230 temperature = 225 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Generic PLA Silk @PGIS] inherits = Generic PLA @PG @@ -6968,19 +7467,19 @@ temperature = 225 slowdown_below_layer_time = 8 filament_max_volumetric_speed = 7 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.035{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PLA Silk @PGIS 0.6] inherits = Generic PLA Silk @PGIS slowdown_below_layer_time = 15 filament_max_volumetric_speed = 9 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Generic PLA Silk @PGIS 0.8] inherits = Generic PLA Silk @PGIS slowdown_below_layer_time = 20 filament_max_volumetric_speed = 12 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:3D-Fuel Standard PLA] inherits = *PLA* @@ -7005,6 +7504,15 @@ filament_max_volumetric_speed = 16 first_layer_temperature = 210 temperature = 210 +[filament:3D-Fuel Standard PLA @XL] +inherits = 3D-Fuel Standard PLA @PG; *PLAXL* + +[filament:3D-Fuel Standard PLA @XL 0.6] +inherits = 3D-Fuel Standard PLA @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Standard PLA @XL 0.8] +inherits = 3D-Fuel Standard PLA @PG 0.8; *PLA08XL* + [filament:3D-Fuel EasiPrint PLA] inherits = 3D-Fuel Standard PLA filament_cost = 30.44 @@ -7023,6 +7531,15 @@ filament_max_volumetric_speed = 16 first_layer_temperature = 210 temperature = 210 +[filament:3D-Fuel EasiPrint PLA @XL] +inherits = 3D-Fuel EasiPrint PLA @PG; *PLAXL* + +[filament:3D-Fuel EasiPrint PLA @XL 0.6] +inherits = 3D-Fuel EasiPrint PLA @PG 0.6; *PLA06XL* + +[filament:3D-Fuel EasiPrint PLA @XL 0.8] +inherits = 3D-Fuel EasiPrint PLA @PG 0.8; *PLA08XL* + [filament:3D-Fuel Pro PLA] inherits = *PLA* filament_vendor = 3D-Fuel @@ -7047,6 +7564,15 @@ filament_max_volumetric_speed = 17 first_layer_temperature = 225 temperature = 225 +[filament:3D-Fuel Pro PLA @XL] +inherits = 3D-Fuel Pro PLA @PG; *PLAXL* + +[filament:3D-Fuel Pro PLA @XL 0.6] +inherits = 3D-Fuel Pro PLA @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Pro PLA @XL 0.8] +inherits = 3D-Fuel Pro PLA @PG 0.8; *PLA08XL* + [filament:3D-Fuel Buzzed] inherits = 3D-Fuel Standard PLA filament_cost = 44.27 @@ -7069,6 +7595,15 @@ filament_max_volumetric_speed = 12 first_layer_temperature = 210 temperature = 210 +[filament:3D-Fuel Buzzed @XL] +inherits = 3D-Fuel Buzzed @PG; *PLAXL* + +[filament:3D-Fuel Buzzed @XL 0.6] +inherits = 3D-Fuel Buzzed @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Buzzed @XL 0.8] +inherits = 3D-Fuel Buzzed @PG 0.8; *PLA08XL* + [filament:3D-Fuel Wound up] inherits = 3D-Fuel Buzzed filament_cost = 44.27 @@ -7091,30 +7626,42 @@ filament_max_volumetric_speed = 12 first_layer_temperature = 220 temperature = 220 +[filament:3D-Fuel Wound up @XL] +inherits = 3D-Fuel Wound up @PG; *PLAXL* + +[filament:3D-Fuel Wound up @XL 0.6] +inherits = 3D-Fuel Wound up @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Wound up @XL 0.8] +inherits = 3D-Fuel Wound up @PG 0.8; *PLA08XL* + [filament:3D-Fuel Workday ABS] inherits = *ABSC* filament_vendor = 3D-Fuel filament_cost = 23.25 filament_density = 1.04 -[filament:3D-Fuel Workday ABS @PG] -inherits = 3D-Fuel Workday ABS; *ABSPG* +[filament:3D-Fuel Workday ABS @XL] +inherits = 3D-Fuel Workday ABS; *ABSPG*; *ABSXL* +renamed_from = "3D-Fuel Workday ABS @PG" bed_temperature = 105 -[filament:3D-Fuel Workday ABS @PG 0.6] -inherits = 3D-Fuel Workday ABS @PG; *ABS06PG* +[filament:3D-Fuel Workday ABS @XL 0.6] +inherits = 3D-Fuel Workday ABS @XL; *ABS06XL* +renamed_from = "3D-Fuel Workday ABS @PG 0.6" -[filament:3D-Fuel Workday ABS @PG 0.8] -inherits = 3D-Fuel Workday ABS @PG; *ABS08PG* +[filament:3D-Fuel Workday ABS @XL 0.8] +inherits = 3D-Fuel Workday ABS @XL; *ABS08XL* +renamed_from = "3D-Fuel Workday ABS @PG 0.8" [filament:3D-Fuel Workday ABS @MK4] -inherits = 3D-Fuel Workday ABS; *ABSMK4* +inherits = 3D-Fuel Workday ABS; *ABSPG* [filament:3D-Fuel Workday ABS @MK4 0.6] -inherits = 3D-Fuel Workday ABS @MK4; *ABS06MK4* +inherits = 3D-Fuel Workday ABS @MK4; *ABS06PG* [filament:3D-Fuel Workday ABS @MK4 0.8] -inherits = 3D-Fuel Workday ABS @MK4; *ABS08MK4* +inherits = 3D-Fuel Workday ABS @MK4; *ABS08PG* [filament:3D-Fuel Workday ABS @MINI] inherits = 3D-Fuel Workday ABS; *ABSMINI* @@ -7138,6 +7685,15 @@ filament_max_volumetric_speed = 14 inherits = Jessie PLA @PG; *PLA08PG* filament_max_volumetric_speed = 17 +[filament:Jessie PLA @XL] +inherits = Jessie PLA @PG; *PLAXL* + +[filament:Jessie PLA @XL 0.6] +inherits = Jessie PLA @PG 0.6; *PLA06XL* + +[filament:Jessie PLA @XL 0.8] +inherits = Jessie PLA @PG 0.8; *PLA08XL* + [filament:Jessie PETG] inherits = *PET* filament_vendor = Printed Solid @@ -7151,7 +7707,7 @@ filament_max_volumetric_speed = 7 [filament:Jessie PETG @PG] inherits = Jessie PETG; *PETPG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 8 [filament:Jessie PETG @PG 0.6] inherits = Jessie PETG @PG; *PET06PG* @@ -7163,6 +7719,15 @@ filament_max_volumetric_speed = 20 first_layer_temperature = 245 temperature = 255 +[filament:Jessie PETG @XL] +inherits = Jessie PETG @PG; *PETXL* + +[filament:Jessie PETG @XL 0.6] +inherits = Jessie PETG @PG 0.6; *PET06XL* + +[filament:Jessie PETG @XL 0.8] +inherits = Jessie PETG @PG 0.8; *PET08XL* + [filament:Jessie PETG @MINI] inherits = Jessie PETG; *PETMINI* @@ -7182,6 +7747,15 @@ inherits = Devil Design PLA; *PLA06PG* [filament:Devil Design PLA @PG 0.8] inherits = Devil Design PLA; *PLA08PG* +[filament:Devil Design PLA @XL] +inherits = Devil Design PLA @PG; *PLAXL* + +[filament:Devil Design PLA @XL 0.6] +inherits = Devil Design PLA @PG 0.6; *PLA06XL* + +[filament:Devil Design PLA @XL 0.8] +inherits = Devil Design PLA @PG 0.8; *PLA08XL* + [filament:Devil Design PETG] inherits = *PET* filament_vendor = Devil Design @@ -7202,6 +7776,15 @@ inherits = Devil Design PETG; *PET06PG* [filament:Devil Design PETG @PG 0.8] inherits = Devil Design PETG; *PET08PG* +[filament:Devil Design PETG @XL] +inherits = Devil Design PETG @PG; *PETXL* + +[filament:Devil Design PETG @XL 0.6] +inherits = Devil Design PETG @PG 0.6; *PET06XL* + +[filament:Devil Design PETG @XL 0.8] +inherits = Devil Design PETG @PG 0.8; *PET08XL* + [filament:Spectrum PLA] inherits = *PLA* filament_vendor = Spectrum @@ -7217,6 +7800,15 @@ inherits = Spectrum PLA; *PLA06PG* [filament:Spectrum PLA @PG 0.8] inherits = Spectrum PLA; *PLA08PG* +[filament:Spectrum PLA @XL] +inherits = Spectrum PLA @PG; *PLAXL* + +[filament:Spectrum PLA @XL 0.6] +inherits = Spectrum PLA @PG 0.6; *PLA06XL* + +[filament:Spectrum PLA @XL 0.8] +inherits = Spectrum PLA @PG 0.8; *PLA08XL* + [filament:Spectrum PETG Matt] inherits = *PET* filament_vendor = Spectrum @@ -7246,6 +7838,15 @@ inherits = Spectrum PETG Matt @PG; *PET06PG* [filament:Spectrum PETG Matt @PG 0.8] inherits = Spectrum PETG Matt @PG; *PET08PG* +[filament:Spectrum PETG Matt @XL] +inherits = Spectrum PETG Matt @PG; *PETXL* + +[filament:Spectrum PETG Matt @XL 0.6] +inherits = Spectrum PETG Matt @PG 0.6; *PET06XL* + +[filament:Spectrum PETG Matt @XL 0.8] +inherits = Spectrum PETG Matt @PG 0.8; *PET08XL* + [filament:Spectrum PETG Matt @MINI] inherits = Spectrum PETG Matt; *PETMINI* @@ -7278,6 +7879,15 @@ inherits = Spectrum PETG HT100 @PG; *PET06PG* [filament:Spectrum PETG HT100 @PG 0.8] inherits = Spectrum PETG HT100 @PG; *PET08PG* +[filament:Spectrum PETG HT100 @XL] +inherits = Spectrum PETG HT100 @PG; *PETXL* + +[filament:Spectrum PETG HT100 @XL 0.6] +inherits = Spectrum PETG HT100 @PG 0.6; *PET06XL* + +[filament:Spectrum PETG HT100 @XL 0.8] +inherits = Spectrum PETG HT100 @PG 0.8; *PET08XL* + [filament:Spectrum PETG HT100 @MINI] inherits = Spectrum PETG HT100; *PETMINI* bed_temperature = 100 @@ -7312,6 +7922,15 @@ inherits = Spectrum GreenyHT @PG; *PLA06PG* [filament:Spectrum GreenyHT @PG 0.8] inherits = Spectrum GreenyHT @PG; *PLA08PG* +[filament:Spectrum GreenyHT @XL] +inherits = Spectrum GreenyHT @PG; *PLAXL* + +[filament:Spectrum GreenyHT @XL 0.6] +inherits = Spectrum GreenyHT @PG 0.6; *PLA06XL* + +[filament:Spectrum GreenyHT @XL 0.8] +inherits = Spectrum GreenyHT @PG 0.8; *PLA08XL* + [filament:Spectrum ASA 275] inherits = *ABSC* filament_vendor = Spectrum @@ -7325,15 +7944,21 @@ filament_density = 1.24 [filament:Spectrum ASA 275 @PG] inherits = Spectrum ASA 275; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Spectrum ASA 275 @PG 0.6] inherits = Spectrum ASA 275 @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Spectrum ASA 275 @PG 0.8] inherits = Spectrum ASA 275 @PG; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Spectrum ASA 275 @XL] +inherits = Spectrum ASA 275; *ABSPG*; *ABSXL* + +[filament:Spectrum ASA 275 @XL 0.6] +inherits = Spectrum ASA 275 @XL; *ABS06XL* + +[filament:Spectrum ASA 275 @XL 0.8] +inherits = Spectrum ASA 275 @XL; *ABS08XL* [filament:Spectrum ASA 275 @MINI] inherits = Spectrum ASA 275; *ABSMINI* @@ -7351,25 +7976,26 @@ filament_type = ASA filament_density = 1.24 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Spectrum ASA Kevlar @PG] -inherits = Spectrum ASA Kevlar; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ +[filament:Spectrum ASA Kevlar @XL] +inherits = Spectrum ASA Kevlar; *ABSPG*; *ABSXL* +renamed_from = "Spectrum ASA Kevlar @PG" -[filament:Spectrum ASA Kevlar @PG 0.6] -inherits = Spectrum ASA Kevlar @PG; *ABS06PG* +[filament:Spectrum ASA Kevlar @XL 0.6] +inherits = Spectrum ASA Kevlar @XL; *ABS06XL* +renamed_from = "Spectrum ASA Kevlar @PG 0.6" -[filament:Spectrum ASA Kevlar @PG 0.8] -inherits = Spectrum ASA Kevlar @PG; *ABS08PG* +[filament:Spectrum ASA Kevlar @XL 0.8] +inherits = Spectrum ASA Kevlar @XL; *ABS08XL* +renamed_from = "Spectrum ASA Kevlar @PG 0.8" [filament:Spectrum ASA Kevlar @MK4] -inherits = Spectrum ASA Kevlar; *ABSMK4* -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ +inherits = Spectrum ASA Kevlar; *ABSPG* [filament:Spectrum ASA Kevlar @MK4 0.6] -inherits = Spectrum ASA Kevlar @MK4; *ABS06MK4* +inherits = Spectrum ASA Kevlar @MK4; *ABS06PG* [filament:Spectrum ASA Kevlar @MK4 0.8] -inherits = Spectrum ASA Kevlar @MK4; *ABS08MK4* +inherits = Spectrum ASA Kevlar @MK4; *ABS08PG* [filament:Spectrum ASA Kevlar @MINI] inherits = Spectrum ASA Kevlar; *ABSMINI* @@ -7396,6 +8022,15 @@ inherits = Spectrum Tough PLA @PG; *PLA06PG* [filament:Spectrum Tough PLA @PG 0.8] inherits = Spectrum Tough PLA @PG; *PLA08PG* +[filament:Spectrum Tough PLA @XL] +inherits = Spectrum Tough PLA @PG; *PLAXL* + +[filament:Spectrum Tough PLA @XL 0.6] +inherits = Spectrum Tough PLA @PG 0.6; *PLA06XL* + +[filament:Spectrum Tough PLA @XL 0.8] +inherits = Spectrum Tough PLA @PG 0.8; *PLA08XL* + [filament:Spectrum PLA PRO] inherits = *PLA* filament_vendor = Spectrum @@ -7411,6 +8046,15 @@ inherits = Spectrum PLA PRO @PG; *PLA06PG* [filament:Spectrum PLA PRO @PG 0.8] inherits = Spectrum PLA PRO @PG; *PLA08PG* +[filament:Spectrum PLA PRO @XL] +inherits = Spectrum PLA PRO @PG; *PLAXL* + +[filament:Spectrum PLA PRO @XL 0.6] +inherits = Spectrum PLA PRO @PG 0.6; *PLA06XL* + +[filament:Spectrum PLA PRO @XL 0.8] +inherits = Spectrum PLA PRO @PG 0.8; *PLA08XL* + [filament:Spectrum PCTG] inherits = *PET* filament_vendor = Spectrum @@ -7428,6 +8072,15 @@ inherits = Spectrum PCTG @PG; *PET06PG* [filament:Spectrum PCTG @PG 0.8] inherits = Spectrum PCTG @PG; *PET08PG* +[filament:Spectrum PCTG @XL] +inherits = Spectrum PCTG @PG; *PETXL* + +[filament:Spectrum PCTG @XL 0.6] +inherits = Spectrum PCTG @PG 0.6; *PET06XL* + +[filament:Spectrum PCTG @XL 0.8] +inherits = Spectrum PCTG @PG 0.8; *PET08XL* + [filament:Spectrum PCTG @MINI] inherits = Spectrum PCTG; *PETMINI* @@ -7442,7 +8095,33 @@ filament_retract_speed = nil filament_retract_lift = nil compatible_printers_condition = nozzle_diameter[0]>=0.35 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material -[filament:Generic FLEX @PG] +[filament:Generic FLEX @XL] +inherits = Generic FLEX; *FLEXXL* +filament_max_volumetric_speed = 3 +filament_retract_length = 2.5 +fan_always_on = 1 +min_fan_speed = 30 +max_fan_speed = 30 +cooling = 1 +filament_retract_lift = 0 +slowdown_below_layer_time = 10 +first_layer_temperature = 230 +temperature = 230 +extrusion_multiplier = 1.08 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 2.5 + +[filament:Generic FLEX @XL 0.6] +inherits = Generic FLEX @XL; *FLEX06XL* +filament_max_volumetric_speed = 4 +filament_multitool_ramming_flow = 4 + +[filament:Generic FLEX @XL 0.8] +inherits = Generic FLEX @XL; *FLEX08XL* +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:Generic FLEX @MK4] inherits = Generic FLEX; *FLEXPG* filament_max_volumetric_speed = 3 filament_retract_length = 2.5 @@ -7456,34 +8135,12 @@ first_layer_temperature = 230 temperature = 230 extrusion_multiplier = 1.08 -[filament:Generic FLEX @PG 0.6] -inherits = Generic FLEX @PG; *FLEX06PG* -filament_max_volumetric_speed = 4 - -[filament:Generic FLEX @PG 0.8] -inherits = Generic FLEX @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:Generic FLEX @MK4] -inherits = Generic FLEX; *FLEXMK4* -filament_max_volumetric_speed = 3 -filament_retract_length = 2.5 -fan_always_on = 1 -min_fan_speed = 30 -max_fan_speed = 30 -cooling = 1 -filament_retract_lift = 0 -slowdown_below_layer_time = 10 -first_layer_temperature = 230 -temperature = 230 -extrusion_multiplier = 1.08 - [filament:Generic FLEX @MK4 0.6] -inherits = Generic FLEX @MK4; *FLEX06MK4* +inherits = Generic FLEX @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Generic FLEX @MK4 0.8] -inherits = Generic FLEX @MK4; *FLEX08MK4* +inherits = Generic FLEX @MK4; *FLEX08PG* filament_max_volumetric_speed = 9 [filament:Fillamentum Flexfill 92A] @@ -7503,32 +8160,36 @@ min_fan_speed = 60 disable_fan_first_layers = 4 full_fan_speed_layer = 6 -[filament:Fillamentum Flexfill 92A @PG] -inherits = Fillamentum Flexfill 92A; *FLEXPG* +[filament:Fillamentum Flexfill 92A @XL] +inherits = Fillamentum Flexfill 92A; *FLEXXL* filament_max_volumetric_speed = 3 extrusion_multiplier = 1.1 filament_retract_length = 3.5 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3.5 -[filament:Fillamentum Flexfill 92A @PG 0.6] -inherits = Fillamentum Flexfill 92A @PG; *FLEX06PG* +[filament:Fillamentum Flexfill 92A @XL 0.6] +inherits = Fillamentum Flexfill 92A @XL; *FLEX06XL* filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:Fillamentum Flexfill 92A @PG 0.8] -inherits = Fillamentum Flexfill 92A @PG; *FLEX08PG* +[filament:Fillamentum Flexfill 92A @XL 0.8] +inherits = Fillamentum Flexfill 92A @XL; *FLEX08XL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fillamentum Flexfill 92A @MK4] -inherits = Fillamentum Flexfill 92A; *FLEXMK4* +inherits = Fillamentum Flexfill 92A; *FLEXPG* filament_max_volumetric_speed = 3.5 extrusion_multiplier = 1.1 filament_retract_length = 3.5 [filament:Fillamentum Flexfill 92A @MK4 0.6] -inherits = Fillamentum Flexfill 92A @MK4; *FLEX06MK4* +inherits = Fillamentum Flexfill 92A @MK4; *FLEX06PG* filament_max_volumetric_speed = 6.5 [filament:Fillamentum Flexfill 92A @MK4 0.8] -inherits = Fillamentum Flexfill 92A @MK4; *FLEX08MK4* +inherits = Fillamentum Flexfill 92A @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:AmazonBasics TPU] @@ -7558,30 +8219,33 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 -[filament:AmazonBasics TPU @PG] +[filament:AmazonBasics TPU @XL] +inherits = AmazonBasics TPU; *FLEXXL* +filament_retract_length = 2.5 +extrusion_multiplier = 1.1 +filament_retract_length_toolchange = 2.5 + +[filament:AmazonBasics TPU @XL 0.6] +inherits = AmazonBasics TPU @XL; *FLEX06XL* +filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 + +[filament:AmazonBasics TPU @XL 0.8] +inherits = AmazonBasics TPU @XL; *FLEX08XL* +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:AmazonBasics TPU @MK4] inherits = AmazonBasics TPU; *FLEXPG* filament_retract_length = 2.5 extrusion_multiplier = 1.1 -[filament:AmazonBasics TPU @PG 0.6] -inherits = AmazonBasics TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:AmazonBasics TPU @PG 0.8] -inherits = AmazonBasics TPU @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:AmazonBasics TPU @MK4] -inherits = AmazonBasics TPU; *FLEXMK4* -filament_retract_length = 2.5 -extrusion_multiplier = 1.1 - [filament:AmazonBasics TPU @MK4 0.6] -inherits = AmazonBasics TPU @MK4; *FLEX06MK4* +inherits = AmazonBasics TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 6.5 [filament:AmazonBasics TPU @MK4 0.8] -inherits = AmazonBasics TPU @MK4; *FLEX08MK4* +inherits = AmazonBasics TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:SainSmart TPU] @@ -7611,34 +8275,38 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 -[filament:SainSmart TPU @PG] +[filament:SainSmart TPU @XL] +inherits = SainSmart TPU; *FLEXXL* +filament_max_volumetric_speed = 5 +first_layer_temperature = 235 +temperature = 235 +filament_retract_length = 1.5 +filament_multitool_ramming_flow = 5 +filament_retract_length_toolchange = 1.5 + +[filament:SainSmart TPU @XL 0.6] +inherits = SainSmart TPU @XL; *FLEX06XL* +filament_max_volumetric_speed = 6 +filament_multitool_ramming_flow = 6 + +[filament:SainSmart TPU @XL 0.8] +inherits = SainSmart TPU @XL; *FLEX08XL* +filament_max_volumetric_speed = 9 +filament_multitool_ramming_flow = 9 + +[filament:SainSmart TPU @MK4] inherits = SainSmart TPU; *FLEXPG* filament_max_volumetric_speed = 5 first_layer_temperature = 235 temperature = 235 filament_retract_length = 1.5 -[filament:SainSmart TPU @PG 0.6] -inherits = SainSmart TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 - -[filament:SainSmart TPU @PG 0.8] -inherits = SainSmart TPU @PG; *FLEX08PG* -filament_max_volumetric_speed = 9 - -[filament:SainSmart TPU @MK4] -inherits = SainSmart TPU; *FLEXMK4* -filament_max_volumetric_speed = 5 -first_layer_temperature = 235 -temperature = 235 -filament_retract_length = 1.5 - [filament:SainSmart TPU @MK4 0.6] -inherits = SainSmart TPU @MK4; *FLEX06MK4* +inherits = SainSmart TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:SainSmart TPU @MK4 0.8] -inherits = SainSmart TPU @MK4; *FLEX08MK4* +inherits = SainSmart TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:NinjaTek NinjaFlex TPU] @@ -7668,32 +8336,36 @@ min_print_speed = 10 slowdown_below_layer_time = 10 cooling = 1 -[filament:NinjaTek NinjaFlex TPU @PG] -inherits = NinjaTek NinjaFlex TPU; *FLEXPG* +[filament:NinjaTek NinjaFlex TPU @XL] +inherits = NinjaTek NinjaFlex TPU; *FLEXXL* filament_max_volumetric_speed = 3 filament_retract_length = 3.5 extrusion_multiplier = 1.12 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3.5 -[filament:NinjaTek NinjaFlex TPU @PG 0.6] -inherits = NinjaTek NinjaFlex TPU @PG; *FLEX06PG* +[filament:NinjaTek NinjaFlex TPU @XL 0.6] +inherits = NinjaTek NinjaFlex TPU @XL; *FLEX06XL* filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:NinjaTek NinjaFlex TPU @PG 0.8] -inherits = NinjaTek NinjaFlex TPU @PG; *FLEX08PG* +[filament:NinjaTek NinjaFlex TPU @XL 0.8] +inherits = NinjaTek NinjaFlex TPU @XL; *FLEX08XL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:NinjaTek NinjaFlex TPU @MK4] -inherits = NinjaTek NinjaFlex TPU; *FLEXMK4* +inherits = NinjaTek NinjaFlex TPU; *FLEXPG* filament_max_volumetric_speed = 3.5 filament_retract_length = 3.5 extrusion_multiplier = 1.12 [filament:NinjaTek NinjaFlex TPU @MK4 0.6] -inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX06MK4* +inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 6.5 [filament:NinjaTek NinjaFlex TPU @MK4 0.8] -inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX08MK4* +inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:NinjaTek Cheetah TPU] @@ -7707,30 +8379,34 @@ filament_deretract_speed = 25 first_layer_temperature = 240 temperature = 240 -[filament:NinjaTek Cheetah TPU @PG] -inherits = NinjaTek Cheetah TPU; *FLEXPG* +[filament:NinjaTek Cheetah TPU @XL] +inherits = NinjaTek Cheetah TPU; *FLEXXL* filament_max_volumetric_speed = 5 filament_retract_length = 2.2 +filament_multitool_ramming_flow = 5 +filament_retract_length_toolchange = 2.2 -[filament:NinjaTek Cheetah TPU @PG 0.6] -inherits = NinjaTek Cheetah TPU @PG; *FLEX06PG* +[filament:NinjaTek Cheetah TPU @XL 0.6] +inherits = NinjaTek Cheetah TPU @XL; *FLEX06XL* filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 -[filament:NinjaTek Cheetah TPU @PG 0.8] -inherits = NinjaTek Cheetah TPU @PG; *FLEX08PG* +[filament:NinjaTek Cheetah TPU @XL 0.8] +inherits = NinjaTek Cheetah TPU @XL; *FLEX08XL* filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 [filament:NinjaTek Cheetah TPU @MK4] -inherits = NinjaTek Cheetah TPU; *FLEXMK4* +inherits = NinjaTek Cheetah TPU; *FLEXPG* filament_max_volumetric_speed = 6 filament_retract_length = 2.2 [filament:NinjaTek Cheetah TPU @MK4 0.6] -inherits = NinjaTek Cheetah TPU @MK4; *FLEX06MK4* +inherits = NinjaTek Cheetah TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 8 [filament:NinjaTek Cheetah TPU @MK4 0.8] -inherits = NinjaTek Cheetah TPU @MK4; *FLEX08MK4* +inherits = NinjaTek Cheetah TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 12 [filament:NinjaTek Cheetah TPU @MINI] @@ -7771,30 +8447,34 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 -[filament:Filatech FilaFlex40 @PG] +[filament:Filatech FilaFlex40 @XL] +inherits = Filatech FilaFlex40; *FLEXXL* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.5 +filament_multitool_ramming_flow = 4 +filament_retract_length_toolchange = 2.5 + +[filament:Filatech FilaFlex40 @XL 0.6] +inherits = Filatech FilaFlex40 @XL; *FLEX06XL* +filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 + +[filament:Filatech FilaFlex40 @XL 0.8] +inherits = Filatech FilaFlex40 @XL; *FLEX08XL* +filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 + +[filament:Filatech FilaFlex40 @MK4] inherits = Filatech FilaFlex40; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 2.5 -[filament:Filatech FilaFlex40 @PG 0.6] -inherits = Filatech FilaFlex40 @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:Filatech FilaFlex40 @PG 0.8] -inherits = Filatech FilaFlex40 @PG; *FLEX08PG* -filament_max_volumetric_speed = 10 - -[filament:Filatech FilaFlex40 @MK4] -inherits = Filatech FilaFlex40; *FLEXMK4* -filament_max_volumetric_speed = 4 -filament_retract_length = 2.5 - [filament:Filatech FilaFlex40 @MK4 0.6] -inherits = Filatech FilaFlex40 @MK4; *FLEX06MK4* +inherits = Filatech FilaFlex40 @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Filatech FilaFlex40 @MK4 0.8] -inherits = Filatech FilaFlex40 @MK4; *FLEX08MK4* +inherits = Filatech FilaFlex40 @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Filatech FilaFlex30] @@ -7804,30 +8484,34 @@ filament_density = 1.15 extrusion_multiplier = 1.1 filament_cost = -[filament:Filatech FilaFlex30 @PG] +[filament:Filatech FilaFlex30 @XL] +inherits = Filatech FilaFlex30; *FLEXXL* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3 +filament_multitool_ramming_flow = 3.5 +filament_retract_length_toolchange = 3 + +[filament:Filatech FilaFlex30 @XL 0.6] +inherits = Filatech FilaFlex30 @XL; *FLEX06XL* +filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 + +[filament:Filatech FilaFlex30 @XL 0.8] +inherits = Filatech FilaFlex30 @XL; *FLEX08XL* +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:Filatech FilaFlex30 @MK4] inherits = Filatech FilaFlex30; *FLEXPG* filament_max_volumetric_speed = 3.5 filament_retract_length = 3 -[filament:Filatech FilaFlex30 @PG 0.6] -inherits = Filatech FilaFlex30 @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:Filatech FilaFlex30 @PG 0.8] -inherits = Filatech FilaFlex30 @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:Filatech FilaFlex30 @MK4] -inherits = Filatech FilaFlex30; *FLEXMK4* -filament_max_volumetric_speed = 3.5 -filament_retract_length = 3 - [filament:Filatech FilaFlex30 @MK4 0.6] -inherits = Filatech FilaFlex30 @MK4; *FLEX06MK4* +inherits = Filatech FilaFlex30 @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:Filatech FilaFlex30 @MK4 0.8] -inherits = Filatech FilaFlex30 @MK4; *FLEX08MK4* +inherits = Filatech FilaFlex30 @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Filatech FilaFlex55] @@ -7870,32 +8554,36 @@ min_fan_speed = 80 fan_always_on = 1 temperature = 235 -[filament:Filatech TPU @PG] -inherits = Filatech TPU; *FLEXPG* +[filament:Filatech TPU @XL] +inherits = Filatech TPU; *FLEXXL* filament_max_volumetric_speed = 4.5 first_layer_temperature = 235 filament_retract_length = 2.2 +filament_multitool_ramming_flow = 4.5 +filament_retract_length_toolchange = 2.2 -[filament:Filatech TPU @PG 0.6] -inherits = Filatech TPU @PG; *FLEX06PG* +[filament:Filatech TPU @XL 0.6] +inherits = Filatech TPU @XL; *FLEX06XL* filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 -[filament:Filatech TPU @PG 0.8] -inherits = Filatech TPU @PG; *FLEX08PG* +[filament:Filatech TPU @XL 0.8] +inherits = Filatech TPU @XL; *FLEX08XL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Filatech TPU @MK4] -inherits = Filatech TPU; *FLEXMK4* +inherits = Filatech TPU; *FLEXPG* filament_max_volumetric_speed = 5.5 first_layer_temperature = 235 filament_retract_length = 2.2 [filament:Filatech TPU @MK4 0.6] -inherits = Filatech TPU @MK4; *FLEX06MK4* +inherits = Filatech TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:Filatech TPU @MK4 0.8] -inherits = Filatech TPU @MK4; *FLEX08MK4* +inherits = Filatech TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Filatech ABS] @@ -7905,24 +8593,27 @@ filament_cost = extrusion_multiplier = 0.95 filament_density = 1.05 -[filament:Filatech ABS @PG] -inherits = Filatech ABS; *ABSPG* +[filament:Filatech ABS @XL] +inherits = Filatech ABS; *ABSPG*; *ABSXL* +renamed_from = "Filatech ABS @PG" bed_temperature = 105 -[filament:Filatech ABS @PG 0.6] -inherits = Filatech ABS @PG; *ABS06PG* +[filament:Filatech ABS @XL 0.6] +inherits = Filatech ABS @XL; *ABS06XL* +renamed_from = "Filatech ABS @PG 0.6" -[filament:Filatech ABS @PG 0.8] -inherits = Filatech ABS @PG; *ABS08PG* +[filament:Filatech ABS @XL 0.8] +inherits = Filatech ABS @XL; *ABS08XL* +renamed_from = "Filatech ABS @PG 0.8" [filament:Filatech ABS @MK4] -inherits = Filatech ABS; *ABSMK4* +inherits = Filatech ABS; *ABSPG* [filament:Filatech ABS @MK4 0.6] -inherits = Filatech ABS @MK4; *ABS06MK4* +inherits = Filatech ABS @MK4; *ABS06PG* [filament:Filatech ABS @MK4 0.8] -inherits = Filatech ABS @MK4; *ABS08MK4* +inherits = Filatech ABS @MK4; *ABS08PG* [filament:Filatech ABS @MINI] inherits = Filatech ABS; *ABSMINI* @@ -7940,15 +8631,21 @@ compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI [filament:Filatech FilaCarbon @PG] inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG* first_layer_bed_temperature = 100 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG 0.6] inherits = Filatech FilaCarbon @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG 0.8] inherits = Filatech FilaCarbon @PG; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Filatech FilaCarbon @XL] +inherits = Filatech FilaCarbon @PG; *ABSXL*; *04PLUSXL* + +[filament:Filatech FilaCarbon @XL 0.6] +inherits = Filatech FilaCarbon @PG 0.6; *ABS06XL* + +[filament:Filatech FilaCarbon @XL 0.8] +inherits = Filatech FilaCarbon @PG 0.8; *ABS08XL* [filament:Filatech FilaCarbon @MINI] inherits = Filatech FilaCarbon; *ABSMINI* @@ -7974,6 +8671,15 @@ inherits = Filatech FilaPLA; *PLA06PG* [filament:Filatech FilaPLA @PG 0.8] inherits = Filatech FilaPLA; *PLA08PG* +[filament:Filatech FilaPLA @XL] +inherits = Filatech FilaPLA @PG; *PLAXL* + +[filament:Filatech FilaPLA @XL 0.6] +inherits = Filatech FilaPLA @PG 0.6; *PLA06XL* + +[filament:Filatech FilaPLA @XL 0.8] +inherits = Filatech FilaPLA @PG 0.8; *PLA08XL* + [filament:Filatech PLA] inherits = *PLA* filament_vendor = Filatech @@ -7991,6 +8697,15 @@ inherits = Filatech PLA; *PLA06PG* [filament:Filatech PLA @PG 0.8] inherits = Filatech PLA; *PLA08PG* +[filament:Filatech PLA @XL] +inherits = Filatech PLA @PG; *PLAXL* + +[filament:Filatech PLA @XL 0.6] +inherits = Filatech PLA @PG 0.6; *PLA06XL* + +[filament:Filatech PLA @XL 0.8] +inherits = Filatech PLA @PG 0.8; *PLA08XL* + [filament:Filatech PLA+] inherits = Filatech PLA filament_density = 1.24 @@ -8004,6 +8719,15 @@ inherits = Filatech PLA+; *PLA06PG* [filament:Filatech PLA+ @PG 0.8] inherits = Filatech PLA+; *PLA08PG* +[filament:Filatech PLA+ @XL] +inherits = Filatech PLA+ @PG; *PLAXL* + +[filament:Filatech PLA+ @XL 0.6] +inherits = Filatech PLA+ @PG; *PLA06XL* + +[filament:Filatech PLA+ @XL 0.8] +inherits = Filatech PLA+ @PG; *PLA08XL* + [filament:Filatech FilaTough] inherits = Filatech ABS filament_cost = @@ -8017,15 +8741,21 @@ cooling = 0 [filament:Filatech FilaTough @PG] inherits = Filatech FilaTough; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaTough @PG 0.6] inherits = Filatech FilaTough; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaTough @PG 0.8] inherits = Filatech FilaTough; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Filatech FilaTough @XL] +inherits = Filatech FilaTough @PG; *ABSXL* + +[filament:Filatech FilaTough @XL 0.6] +inherits = Filatech FilaTough @PG 0.6; *ABS06XL* + +[filament:Filatech FilaTough @XL 0.8] +inherits = Filatech FilaTough @PG 0.8; *ABS08XL* [filament:Filatech HIPS] inherits = Prusa HIPS @@ -8039,8 +8769,9 @@ temperature = 225 bed_temperature = 110 compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Filatech HIPS @PG] -inherits = Filatech HIPS; *ABSPG* +[filament:Filatech HIPS @XL] +inherits = Filatech HIPS; *ABSPG*; *ABSXL* +renamed_from = "Filatech HIPS @PG" bridge_fan_speed = 50 cooling = 1 extrusion_multiplier = 1 @@ -8053,14 +8784,16 @@ min_fan_speed = 20 first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Filatech HIPS @PG 0.6] -inherits = Filatech HIPS @PG; *ABS06PG* +[filament:Filatech HIPS @XL 0.6] +inherits = Filatech HIPS @XL; *ABS06XL* +renamed_from = "Filatech HIPS @PG 0.6" -[filament:Filatech HIPS @PG 0.8] -inherits = Filatech HIPS @PG; *ABS08PG* +[filament:Filatech HIPS @XL 0.8] +inherits = Filatech HIPS @XL; *ABS08XL* +renamed_from = "Filatech HIPS @PG 0.8" [filament:Filatech HIPS @MK4] -inherits = Filatech HIPS; *ABSMK4* +inherits = Filatech HIPS; *ABSPG* bridge_fan_speed = 50 cooling = 1 extrusion_multiplier = 1 @@ -8072,10 +8805,10 @@ max_fan_speed = 20 min_fan_speed = 20 [filament:Filatech HIPS @MK4 0.6] -inherits = Filatech HIPS @MK4; *ABS06MK4* +inherits = Filatech HIPS @MK4; *ABS06PG* [filament:Filatech HIPS @MK4 0.8] -inherits = Filatech HIPS @MK4; *ABS08MK4* +inherits = Filatech HIPS @MK4; *ABS08PG* [filament:Filatech HIPS @MINI] inherits = Filatech HIPS; *ABSMINI* @@ -8096,30 +8829,33 @@ filament_type = PA filament_max_volumetric_speed = 8 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Filatech PA @PG] -inherits = Filatech PA; *ABSPG* +[filament:Filatech PA @XL] +inherits = Filatech PA; *ABSPG*; *ABSXL* +renamed_from = "Filatech PA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 -[filament:Filatech PA @PG 0.6] -inherits = Filatech PA @PG; *ABS06PG* +[filament:Filatech PA @XL 0.6] +inherits = Filatech PA @XL; *ABS06XL* +renamed_from = "Filatech PA @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Filatech PA @PG 0.8] -inherits = Filatech PA @PG; *ABS08PG* +[filament:Filatech PA @XL 0.8] +inherits = Filatech PA @XL; *ABS08XL* +renamed_from = "Filatech PA @PG 0.8" filament_max_volumetric_speed = 12 [filament:Filatech PA @MK4] -inherits = Filatech PA; *ABSMK4* +inherits = Filatech PA; *ABSPG* filament_max_volumetric_speed = 8 [filament:Filatech PA @MK4 0.6] -inherits = Filatech PA @MK4; *ABS06MK4* +inherits = Filatech PA @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Filatech PA @MK4 0.8] -inherits = Filatech PA @MK4; *ABS08MK4* +inherits = Filatech PA @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Filatech PA @MK2] @@ -8141,23 +8877,26 @@ bed_temperature = 115 filament_density = 1.2 filament_type = PC -[filament:Filatech PC @PG] -inherits = Filatech PC; *PCPG* +[filament:Filatech PC @XL] +inherits = Filatech PC; *PCPG*; *PCXL* +renamed_from = "Filatech PC @PG" -[filament:Filatech PC @PG 0.6] -inherits = Filatech PC @PG; *PC06PG* +[filament:Filatech PC @XL 0.6] +inherits = Filatech PC @XL; *PC06XL* +renamed_from = "Filatech PC @PG 0.6" -[filament:Filatech PC @PG 0.8] -inherits = Filatech PC @PG; *PC08PG* +[filament:Filatech PC @XL 0.8] +inherits = Filatech PC @XL; *PC08XL* +renamed_from = "Filatech PC @PG 0.8" [filament:Filatech PC @MK4] -inherits = Filatech PC; *PCMK4* +inherits = Filatech PC; *PCPG* [filament:Filatech PC @MK4 0.6] -inherits = Filatech PC @MK4; *PC06MK4* +inherits = Filatech PC @MK4; *PC06PG* [filament:Filatech PC @MK4 0.8] -inherits = Filatech PC @MK4; *PC08MK4* +inherits = Filatech PC @MK4; *PC08PG* [filament:Filatech PC @MK2] inherits = Filatech PC @@ -8178,23 +8917,26 @@ cooling = 1 extrusion_multiplier = 0.95 disable_fan_first_layers = 6 -[filament:Filatech PC-ABS @PG] -inherits = Filatech PC-ABS; *PCPG* +[filament:Filatech PC-ABS @XL] +inherits = Filatech PC-ABS; *PCPG*; *PCXL* +renamed_from = "Filatech PC-ABS @PG" -[filament:Filatech PC-ABS @PG 0.6] -inherits = Filatech PC-ABS; *PC06PG* +[filament:Filatech PC-ABS @XL 0.6] +inherits = Filatech PC-ABS @XL; *PC06XL* +renamed_from = "Filatech PC-ABS @PG 0.6" -[filament:Filatech PC-ABS @PG 0.8] -inherits = Filatech PC-ABS; *PC08PG* +[filament:Filatech PC-ABS @XL 0.8] +inherits = Filatech PC-ABS @XL; *PC08XL* +renamed_from = "Filatech PC-ABS @PG 0.8" [filament:Filatech PC-ABS @MK4] -inherits = Filatech PC-ABS; *PCMK4* +inherits = Filatech PC-ABS; *PCPG* [filament:Filatech PC-ABS @MK4 0.6] -inherits = Filatech PC-ABS @MK4; *PC06MK4* +inherits = Filatech PC-ABS @MK4; *PC06PG* [filament:Filatech PC-ABS @MK4 0.8] -inherits = Filatech PC-ABS @MK4; *PC08MK4* +inherits = Filatech PC-ABS @MK4; *PC08PG* [filament:Filatech PC-ABS @MK2] inherits = Filatech PC-ABS @@ -8223,6 +8965,15 @@ inherits = Filatech PETG; *PET06PG* [filament:Filatech PETG @PG 0.8] inherits = Filatech PETG; *PET08PG* +[filament:Filatech PETG @XL] +inherits = Filatech PETG @PG; *PETXL* + +[filament:Filatech PETG @XL 0.6] +inherits = Filatech PETG @PG 0.6; *PET06XL* + +[filament:Filatech PETG @XL 0.8] +inherits = Filatech PETG @PG 0.8; *PET08XL* + [filament:Filatech PETG @MINI] inherits = Filatech PETG; *PETMINI* @@ -8241,6 +8992,15 @@ inherits = Filatech Wood-PLA; *PLA06PG* [filament:Filatech Wood-PLA @PG 0.8] inherits = Filatech Wood-PLA; *PLA08PG* +[filament:Filatech Wood-PLA @XL] +inherits = Filatech Wood-PLA @PG; *PLAXL* + +[filament:Filatech Wood-PLA @XL 0.6] +inherits = Filatech Wood-PLA @PG 0.6; *PLA06XL* + +[filament:Filatech Wood-PLA @XL 0.8] +inherits = Filatech Wood-PLA @PG 0.8; *PLA08XL* + [filament:Ultrafuse PET] inherits = *PET* filament_vendor = BASF @@ -8275,6 +9035,15 @@ inherits = Ultrafuse PET; *PET06PG* [filament:Ultrafuse PET @PG 0.8] inherits = Ultrafuse PET; *PET08PG* +[filament:Ultrafuse PET @XL] +inherits = Ultrafuse PET @PG; *PETXL* + +[filament:Ultrafuse PET @XL 0.6] +inherits = Ultrafuse PET @PG 0.6; *PET06XL* + +[filament:Ultrafuse PET @XL 0.8] +inherits = Ultrafuse PET @PG 0.8; *PET08XL* + [filament:Ultrafuse PET @MINI] inherits = Ultrafuse PET; *PETMINI* @@ -8302,6 +9071,15 @@ inherits = Ultrafuse PRO1; *PLA06PG* [filament:Ultrafuse PRO1 @PG 0.8] inherits = Ultrafuse PRO1; *PLA08PG* +[filament:Ultrafuse PRO1 @XL] +inherits = Ultrafuse PRO1 @PG; *PLAXL* + +[filament:Ultrafuse PRO1 @XL 0.6] +inherits = Ultrafuse PRO1 @PG 0.6; *PLA06XL* + +[filament:Ultrafuse PRO1 @XL 0.8] +inherits = Ultrafuse PRO1 @PG 0.8; *PLA08XL* + [filament:Ultrafuse PRO1 @MINI] inherits = Ultrafuse PRO1 filament_retract_length = nil @@ -8325,25 +9103,28 @@ filament_retract_before_travel = 2 filament_wipe = 0 filament_retract_layer_change = 0 -[filament:Ultrafuse ABS @PG] -inherits = Ultrafuse ABS; *ABSPG* +[filament:Ultrafuse ABS @XL] +inherits = Ultrafuse ABS; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse ABS @PG" first_layer_bed_temperature = 100 bed_temperature = 100 -[filament:Ultrafuse ABS @PG 0.6] -inherits = Ultrafuse ABS @PG; *ABS06PG* +[filament:Ultrafuse ABS @XL 0.6] +inherits = Ultrafuse ABS @XL; *ABS06XL* +renamed_from = "Ultrafuse ABS @PG 0.6" -[filament:Ultrafuse ABS @PG 0.8] -inherits = Ultrafuse ABS @PG; *ABS08PG* +[filament:Ultrafuse ABS @XL 0.8] +inherits = Ultrafuse ABS @XL; *ABS08XL* +renamed_from = "Ultrafuse ABS @PG 0.8" [filament:Ultrafuse ABS @MK4] -inherits = Ultrafuse ABS; *ABSMK4* +inherits = Ultrafuse ABS; *ABSPG* [filament:Ultrafuse ABS @MK4 0.6] -inherits = Ultrafuse ABS @MK4; *ABS06MK4* +inherits = Ultrafuse ABS @MK4; *ABS06PG* [filament:Ultrafuse ABS @MK4 0.8] -inherits = Ultrafuse ABS @MK4; *ABS08MK4* +inherits = Ultrafuse ABS @MK4; *ABS08PG* [filament:Ultrafuse ABS @MINI] inherits = Ultrafuse ABS; *ABSMINI* @@ -8362,25 +9143,28 @@ filament_wipe = nil filament_retract_layer_change = 0 filament_retract_lift = 0 -[filament:Ultrafuse ABS Fusion+ @PG] -inherits = Ultrafuse ABS Fusion+; *ABSPG* +[filament:Ultrafuse ABS Fusion+ @XL] +inherits = Ultrafuse ABS Fusion+; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse ABS Fusion+ @PG" first_layer_bed_temperature = 100 bed_temperature = 100 -[filament:Ultrafuse ABS Fusion+ @PG 0.6] -inherits = Ultrafuse ABS Fusion+ @PG; *ABS06PG* +[filament:Ultrafuse ABS Fusion+ @XL 0.6] +inherits = Ultrafuse ABS Fusion+ @XL; *ABS06XL* +renamed_from = "Ultrafuse ABS Fusion+ @PG 0.6" -[filament:Ultrafuse ABS Fusion+ @PG 0.8] -inherits = Ultrafuse ABS Fusion+ @PG; *ABS08PG* +[filament:Ultrafuse ABS Fusion+ @XL 0.8] +inherits = Ultrafuse ABS Fusion+ @XL; *ABS08XL* +renamed_from = "Ultrafuse ABS Fusion+ @PG 0.8" [filament:Ultrafuse ABS Fusion+ @MK4] -inherits = Ultrafuse ABS Fusion+; *ABSMK4* +inherits = Ultrafuse ABS Fusion+; *ABSPG* [filament:Ultrafuse ABS Fusion+ @MK4 0.6] -inherits = Ultrafuse ABS Fusion+ @MK4; *ABS06MK4* +inherits = Ultrafuse ABS Fusion+ @MK4; *ABS06PG* [filament:Ultrafuse ABS Fusion+ @MK4 0.8] -inherits = Ultrafuse ABS Fusion+ @MK4; *ABS08MK4* +inherits = Ultrafuse ABS Fusion+ @MK4; *ABS08PG* [filament:Ultrafuse ABS Fusion+ @MINI] inherits = Ultrafuse ABS Fusion+; *ABSMINI* @@ -8403,34 +9187,37 @@ disable_fan_first_layers = 4 filament_max_volumetric_speed = 5 filament_notes = "Material Description\nUltrafuse ASA is a high-performance thermoplastic with similar mechanical properties as ABS. ASA offers additional benefits such as high outdoor weather resistance. The UV resistance, toughness, and rigidity make it an ideal material to 3D-print outdoor fixtures and appliances without losing its properties or color. When also taking into account the high heat resistance and high chemical resistance, this filament is a good choice for many types of applications.\n\nPrinting Recommendations:\nApply Magigoo PC, 3D lac or Dimafix to a clean build plate to improve adhesion." -[filament:Ultrafuse ASA @PG] -inherits = Ultrafuse ASA; *ABSPG* +[filament:Ultrafuse ASA @XL] +inherits = Ultrafuse ASA; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse ASA @PG" first_layer_bed_temperature = 105 bed_temperature = 105 filament_max_volumetric_speed = 5 min_fan_speed = 15 max_fan_speed = 40 -[filament:Ultrafuse ASA @PG 0.6] -inherits = Ultrafuse ASA @PG; *ABS06PG* +[filament:Ultrafuse ASA @XL 0.6] +inherits = Ultrafuse ASA @XL; *ABS06XL* +renamed_from = "Ultrafuse ASA @PG 0.6" filament_max_volumetric_speed = 9 -[filament:Ultrafuse ASA @PG 0.8] -inherits = Ultrafuse ASA @PG; *ABS08PG* +[filament:Ultrafuse ASA @XL 0.8] +inherits = Ultrafuse ASA @XL; *ABS08XL* +renamed_from = "Ultrafuse ASA @PG 0.8" filament_max_volumetric_speed = 12 [filament:Ultrafuse ASA @MK4] -inherits = Ultrafuse ASA; *ABSMK4* +inherits = Ultrafuse ASA; *ABSPG* filament_max_volumetric_speed = 5 min_fan_speed = 15 max_fan_speed = 40 [filament:Ultrafuse ASA @MK4 0.6] -inherits = Ultrafuse ASA @MK4; *ABS06MK4* +inherits = Ultrafuse ASA @MK4; *ABS06PG* filament_max_volumetric_speed = 9 [filament:Ultrafuse ASA @MK4 0.8] -inherits = Ultrafuse ASA @MK4; *ABS08MK4* +inherits = Ultrafuse ASA @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Ultrafuse ASA @MINI] @@ -8447,25 +9234,28 @@ max_fan_speed = 20 filament_soluble = 1 filament_notes = "Material Description\nUltrafuse HIPS is a high-quality engineering thermoplastic, which is well known in the 3D-printing industry as a support material for ABS. But this material has additional properties to offer like good impact resistance, good dimensional stability, and easy post-processing. HiPS is a great material to use as a support for ABS because there is a good compatibility between the two materials, and HIPS is an easy breakaway support. Now you have the opportunity to create ABS models with complex geometry. HIPS is easy to post process with glue or with sanding paper." -[filament:Ultrafuse HIPS @PG] -inherits = Ultrafuse HIPS; *ABSPG* +[filament:Ultrafuse HIPS @XL] +inherits = Ultrafuse HIPS; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse HIPS @PG" first_layer_bed_temperature = 100 bed_temperature = 100 -[filament:Ultrafuse HIPS @PG 0.6] -inherits = Ultrafuse HIPS @PG; *ABS06PG* +[filament:Ultrafuse HIPS @XL 0.6] +inherits = Ultrafuse HIPS @XL; *ABS06XL* +renamed_from = "Ultrafuse HIPS @PG 0.6" -[filament:Ultrafuse HIPS @PG 0.8] -inherits = Ultrafuse HIPS @PG; *ABS08PG* +[filament:Ultrafuse HIPS @XL 0.8] +inherits = Ultrafuse HIPS @XL; *ABS08XL* +renamed_from = "Ultrafuse HIPS @PG 0.8" [filament:Ultrafuse HIPS @MK4] -inherits = Ultrafuse HIPS; *ABSMK4* +inherits = Ultrafuse HIPS; *ABSPG* [filament:Ultrafuse HIPS @MK4 0.6] -inherits = Ultrafuse HIPS @MK4; *ABS06MK4* +inherits = Ultrafuse HIPS @MK4; *ABS06PG* [filament:Ultrafuse HIPS @MK4 0.8] -inherits = Ultrafuse HIPS @MK4; *ABS08MK4* +inherits = Ultrafuse HIPS @MK4; *ABS08PG* [filament:Ultrafuse HIPS @MINI] inherits = Ultrafuse HIPS; *ABSMINI* @@ -8502,17 +9292,23 @@ filament_notes = "Material Description\nThe key features of Ultrafuse PA are the [filament:Ultrafuse PA @PG] inherits = Ultrafuse PA; *ABSPG* filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA @PG 0.6] inherits = Ultrafuse PA @PG; *ABS06PG* filament_max_volumetric_speed = 10 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA @PG 0.8] inherits = Ultrafuse PA @PG; *ABS08PG* filament_max_volumetric_speed = 12 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PA @XL] +inherits = Ultrafuse PA @PG; *ABSXL* + +[filament:Ultrafuse PA @XL 0.6] +inherits = Ultrafuse PA @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PA @XL 0.8] +inherits = Ultrafuse PA @PG 0.8; *ABS08XL* [filament:Ultrafuse PA6 GF30] inherits = Ultrafuse PA @@ -8550,6 +9346,12 @@ first_layer_temperature = 275 temperature = 275 compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material +[filament:Ultrafuse PA6 GF30 @XL 0.6] +inherits = Ultrafuse PA6 GF30 @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PA6 GF30 @XL 0.8] +inherits = Ultrafuse PA6 GF30 @PG 0.8; *ABS08XL* + [filament:Ultrafuse PAHT-CF15] inherits = Ultrafuse PA6 GF30 filament_density = 1.23 @@ -8557,11 +9359,15 @@ filament_notes = "Material Description\nPAHT CF15 is a high-performance 3D print [filament:Ultrafuse PAHT-CF15 @PG 0.6] inherits = Ultrafuse PAHT-CF15; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PAHT-CF15 @PG 0.8] inherits = Ultrafuse PAHT-CF15; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PAHT-CF15 @XL 0.6] +inherits = Ultrafuse PAHT-CF15 @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PAHT-CF15 @XL 0.8] +inherits = Ultrafuse PAHT-CF15 @PG 0.8; *ABS08XL* [filament:Ultrafuse PC-ABS-FR] inherits = Ultrafuse ABS @@ -8582,30 +9388,33 @@ compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.* filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS – two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion." start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -[filament:Ultrafuse PC-ABS-FR @PG] -inherits = Ultrafuse PC-ABS-FR; *ABSPG* +[filament:Ultrafuse PC-ABS-FR @XL] +inherits = Ultrafuse PC-ABS-FR; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse PC-ABS-FR @PG" first_layer_bed_temperature = 105 bed_temperature = 105 filament_max_volumetric_speed = 8 -[filament:Ultrafuse PC-ABS-FR @PG 0.6] -inherits = Ultrafuse PC-ABS-FR @PG; *ABS06PG* +[filament:Ultrafuse PC-ABS-FR @XL 0.6] +inherits = Ultrafuse PC-ABS-FR @XL; *ABS06XL* +renamed_from = "Ultrafuse PC-ABS-FR @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Ultrafuse PC-ABS-FR @PG 0.8] -inherits = Ultrafuse PC-ABS-FR @PG; *ABS08PG* +[filament:Ultrafuse PC-ABS-FR @XL 0.8] +inherits = Ultrafuse PC-ABS-FR @XL; *ABS08XL* +renamed_from = "Ultrafuse PC-ABS-FR @PG 0.8" filament_max_volumetric_speed = 12 [filament:Ultrafuse PC-ABS-FR @MK4] -inherits = Ultrafuse PC-ABS-FR; *ABSMK4* +inherits = Ultrafuse PC-ABS-FR; *ABSPG* filament_max_volumetric_speed = 8 [filament:Ultrafuse PC-ABS-FR @MK4 0.6] -inherits = Ultrafuse PC-ABS-FR @MK4; *ABS06MK4* +inherits = Ultrafuse PC-ABS-FR @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Ultrafuse PC-ABS-FR @MK4 0.8] -inherits = Ultrafuse PC-ABS-FR @MK4; *ABS08MK4* +inherits = Ultrafuse PC-ABS-FR @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Ultrafuse PET-CF15] @@ -8643,6 +9452,12 @@ filament_max_volumetric_speed = 13 first_layer_temperature = 270 temperature = 275 +[filament:Ultrafuse PET-CF15 @XL 0.6] +inherits = Ultrafuse PET-CF15 @PG 0.6; *PET06XL* + +[filament:Ultrafuse PET-CF15 @XL 0.8] +inherits = Ultrafuse PET-CF15 @PG 0.8; *PET08XL* + [filament:Ultrafuse PLA] inherits = *PLA* filament_vendor = BASF @@ -8659,6 +9474,15 @@ inherits = Ultrafuse PLA; *PLA06PG* [filament:Ultrafuse PLA @PG 0.8] inherits = Ultrafuse PLA; *PLA08PG* +[filament:Ultrafuse PLA @XL] +inherits = Ultrafuse PLA @PG; *PLAXL* + +[filament:Ultrafuse PLA @XL 0.6] +inherits = Ultrafuse PLA @PG 0.6; *PLA06XL* + +[filament:Ultrafuse PLA @XL 0.8] +inherits = Ultrafuse PLA @PG 0.8; *PLA08XL* + [filament:Ultrafuse PP] inherits = Ultrafuse ABS filament_density = 0.91 @@ -8687,17 +9511,23 @@ compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM [filament:Ultrafuse PP @PG] inherits = Ultrafuse PP; *ABSPG* filament_max_volumetric_speed = 2.5 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG 0.6] inherits = Ultrafuse PP @PG; *ABS06PG* filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG 0.8] inherits = Ultrafuse PP @PG; *ABS08PG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PP @XL] +inherits = Ultrafuse PP; *ABSPG*; *ABSXL* + +[filament:Ultrafuse PP @XL 0.6] +inherits = Ultrafuse PP @XL; *ABS06XL* + +[filament:Ultrafuse PP @XL 0.8] +inherits = Ultrafuse PP @XL; *ABS08XL* [filament:Ultrafuse PP-GF30] inherits = Ultrafuse PP @@ -8723,11 +9553,15 @@ compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI [filament:Ultrafuse PP-GF30 @PG 0.6] inherits = Ultrafuse PP; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP-GF30 @PG 0.8] inherits = Ultrafuse PP; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PP-GF30 @XL 0.6] +inherits = Ultrafuse PP-GF30 @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PP-GF30 @XL 0.8] +inherits = Ultrafuse PP-GF30 @PG 0.8; *ABS08XL* [filament:Ultrafuse TPC-45D] inherits = *FLEX* @@ -8808,7 +9642,30 @@ first_layer_temperature = 225 temperature = 220 filament_notes = "Material Description\nUltrafuse® TPU 85A comes in its natural white color. Chemical properties (e.g. resistance against particular substances) and tolerance for solvents can be made available, if these factors are relevant for a specific application. Generally, these properties correspond to publicly available data on polyether based TPUs. This material is not FDA conform. Good flexibility at low temperature, good wear performance and good damping behavior are the key features of Ultrafuse® TPU 85A.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." -[filament:Ultrafuse TPU-85A @PG] +[filament:Ultrafuse TPU-85A @XL] +inherits = Ultrafuse TPU-85A; *FLEXXL* +renamed_from = "Ultrafuse TPU-85A @PG" +filament_max_volumetric_speed = 3 +extrusion_multiplier = 1.1 +first_layer_temperature = 220 +temperature = 215 +filament_retract_length = 3.5 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3.5 + +[filament:Ultrafuse TPU-85A @XL 0.6] +inherits = Ultrafuse TPU-85A @XL; *FLEX06XL* +renamed_from = "Ultrafuse TPU-85A @PG 0.6" +filament_max_volumetric_speed = 4 +filament_multitool_ramming_flow = 4 + +[filament:Ultrafuse TPU-85A @XL 0.8] +inherits = Ultrafuse TPU-85A @XL; *FLEX08XL* +renamed_from = "Ultrafuse TPU-85A @PG 0.8" +filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 + +[filament:Ultrafuse TPU-85A @MK4] inherits = Ultrafuse TPU-85A; *FLEXPG* filament_max_volumetric_speed = 3 extrusion_multiplier = 1.1 @@ -8816,28 +9673,12 @@ first_layer_temperature = 220 temperature = 215 filament_retract_length = 3.5 -[filament:Ultrafuse TPU-85A @PG 0.6] -inherits = Ultrafuse TPU-85A @PG; *FLEX06PG* -filament_max_volumetric_speed = 4 - -[filament:Ultrafuse TPU-85A @PG 0.8] -inherits = Ultrafuse TPU-85A @PG; *FLEX08PG* -filament_max_volumetric_speed = 7 - -[filament:Ultrafuse TPU-85A @MK4] -inherits = Ultrafuse TPU-85A; *FLEXMK4* -filament_max_volumetric_speed = 3 -extrusion_multiplier = 1.1 -first_layer_temperature = 220 -temperature = 215 -filament_retract_length = 3.5 - [filament:Ultrafuse TPU-85A @MK4 0.6] -inherits = Ultrafuse TPU-85A @MK4; *FLEX06MK4* +inherits = Ultrafuse TPU-85A @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Ultrafuse TPU-85A @MK4 0.8] -inherits = Ultrafuse TPU-85A @MK4; *FLEX08MK4* +inherits = Ultrafuse TPU-85A @MK4; *FLEX08PG* filament_max_volumetric_speed = 8 [filament:Ultrafuse TPU-95A] @@ -8847,30 +9688,37 @@ first_layer_temperature = 230 temperature = 225 filament_notes = "Material Description\nUltrafuse® TPU 95A comes with a well-balanced profile of flexibility and durability. On top of that, it allows for easier and faster printing then softer TPU grades. Parts printed with Ultrafuse® TPU 95A show a high elongation, good impact resistance, excellent layer adhesion and a good resistance to oils and common industrially used chemicals. Due to its good printing behavior, Ultrafuse® TPU 95A is a good choice for starting printing flexible materials on both direct drive and bowden style printers.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." -[filament:Ultrafuse TPU-95A @PG] +[filament:Ultrafuse TPU-95A @XL] +inherits = Ultrafuse TPU-95A; *FLEXXL* +renamed_from = "Ultrafuse TPU-95A @PG" +filament_max_volumetric_speed = 2.5 +filament_retract_length = 3 +filament_multitool_ramming_flow = 2.5 +filament_retract_length_toolchange = 3 + +[filament:Ultrafuse TPU-95A @XL 0.6] +inherits = Ultrafuse TPU-95A @XL; *FLEX06XL* +renamed_from = "Ultrafuse TPU-95A @PG 0.6" +filament_max_volumetric_speed = 3.5 +filament_multitool_ramming_flow = 3.5 + +[filament:Ultrafuse TPU-95A @XL 0.8] +inherits = Ultrafuse TPU-95A @XL; *FLEX08XL* +renamed_from = "Ultrafuse TPU-95A @PG 0.8" +filament_max_volumetric_speed = 6 +filament_multitool_ramming_flow = 6 + +[filament:Ultrafuse TPU-95A @MK4] inherits = Ultrafuse TPU-95A; *FLEXPG* filament_max_volumetric_speed = 2.5 filament_retract_length = 3 -[filament:Ultrafuse TPU-95A @PG 0.6] -inherits = Ultrafuse TPU-95A @PG; *FLEX06PG* -filament_max_volumetric_speed = 3.5 - -[filament:Ultrafuse TPU-95A @PG 0.8] -inherits = Ultrafuse TPU-95A @PG; *FLEX08PG* -filament_max_volumetric_speed = 6 - -[filament:Ultrafuse TPU-95A @MK4] -inherits = Ultrafuse TPU-95A; *FLEXMK4* -filament_max_volumetric_speed = 2.5 -filament_retract_length = 3 - [filament:Ultrafuse TPU-95A @MK4 0.6] -inherits = Ultrafuse TPU-95A @MK4; *FLEX06MK4* +inherits = Ultrafuse TPU-95A @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Ultrafuse TPU-95A @MK4 0.8] -inherits = Ultrafuse TPU-95A @MK4; *FLEX08MK4* +inherits = Ultrafuse TPU-95A @MK4; *FLEX08PG* filament_max_volumetric_speed = 7 [filament:Ultrafuse rPET] @@ -8903,6 +9751,15 @@ filament_max_volumetric_speed = 18 first_layer_temperature = 235 temperature = 245 +[filament:Ultrafuse rPET @XL] +inherits = Ultrafuse rPET @PG; *PETXL* + +[filament:Ultrafuse rPET @XL 0.6] +inherits = Ultrafuse rPET @PG 0.6; *PET06XL* + +[filament:Ultrafuse rPET @XL 0.8] +inherits = Ultrafuse rPET @PG 0.8; *PET08XL* + [filament:Ultrafuse Metal] inherits = *ABSC* renamed_from = "Ultrafuse 17-4 PH" @@ -8929,13 +9786,17 @@ filament_colour = #FFFFFF inherits = Ultrafuse Metal; *ABSPG*; *04PLUSPG* filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K0" -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse Metal @PG 0.6] inherits = Ultrafuse Metal @PG; *ABS06PG* filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K0" -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse Metal @XL] +inherits = Ultrafuse Metal @PG; *ABSXL* + +[filament:Ultrafuse Metal @XL 0.6] +inherits = Ultrafuse Metal @PG 0.6; *ABS06XL* [filament:Polymaker PC-Max] inherits = *ABS* @@ -8951,30 +9812,33 @@ temperature = 270 bridge_fan_speed = 0 filament_max_volumetric_speed = 8 -[filament:Polymaker PC-Max @PG] -inherits = Polymaker PC-Max; *ABSPG* +[filament:Polymaker PC-Max @XL] +inherits = Polymaker PC-Max; *ABSXL* +renamed_from = "Polymaker PC-Max @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 -[filament:Polymaker PC-Max @PG 0.6] -inherits = Polymaker PC-Max @PG; *ABS06PG* +[filament:Polymaker PC-Max @XL 0.6] +inherits = Polymaker PC-Max @XL; *ABS06XL* +renamed_from = "Polymaker PC-Max @PG 0.6" filament_max_volumetric_speed = 12 -[filament:Polymaker PC-Max @PG 0.8] -inherits = Polymaker PC-Max @PG; *ABS08PG* +[filament:Polymaker PC-Max @XL 0.8] +inherits = Polymaker PC-Max @XL; *ABS08XL* +renamed_from = "Polymaker PC-Max @PG 0.8" filament_max_volumetric_speed = 15 [filament:Polymaker PC-Max @MK4] -inherits = Polymaker PC-Max; *ABSMK4* +inherits = Polymaker PC-Max; *ABSPG* filament_max_volumetric_speed = 8 [filament:Polymaker PC-Max @MK4 0.6] -inherits = Polymaker PC-Max @MK4; *ABS06MK4* +inherits = Polymaker PC-Max @MK4; *ABS06PG* filament_max_volumetric_speed = 12 [filament:Polymaker PC-Max @MK4 0.8] -inherits = Polymaker PC-Max @MK4; *ABS08MK4* +inherits = Polymaker PC-Max @MK4; *ABS08PG* filament_max_volumetric_speed = 15 [filament:PrimaSelect PVA+] @@ -9010,6 +9874,15 @@ first_layer_temperature = 210 temperature = 210 filament_max_volumetric_speed = 8 +[filament:PrimaSelect PVA+ @XL] +inherits = PrimaSelect PVA+ @PG; *PLAXL*; *PVAXL* + +[filament:PrimaSelect PVA+ @XL 0.6] +inherits = PrimaSelect PVA+ @PG 0.6; *PLA06XL*; *PVAXL* + +[filament:PrimaSelect PVA+ @XL 0.8] +inherits = PrimaSelect PVA+ @PG 0.8; *PLA08XL*; *PVAXL* + [filament:Prusa ABS] inherits = *ABSC* filament_vendor = Made for Prusa @@ -9018,26 +9891,29 @@ filament_density = 1.08 filament_spool_weight = 230 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusa ABS @PG] -inherits = Prusa ABS; *ABSPG* +[filament:Prusa ABS @XL] +inherits = Prusa ABS; *ABSPG*; *ABSXL* +renamed_from = "Prusa ABS @PG" bed_temperature = 105 -[filament:Prusa ABS @PG 0.6] -inherits = Prusa ABS @PG; *ABS06PG* +[filament:Prusa ABS @XL 0.6] +inherits = Prusa ABS @XL; *ABS06XL* +renamed_from = "Prusa ABS @PG 0.6" -[filament:Prusa ABS @PG 0.8] -inherits = Prusa ABS @PG; *ABS08PG* +[filament:Prusa ABS @XL 0.8] +inherits = Prusa ABS @XL; *ABS08XL* +renamed_from = "Prusa ABS @PG 0.8" first_layer_temperature = 265 temperature = 265 [filament:Prusa ABS @MK4] -inherits = Prusa ABS; *ABSMK4* +inherits = Prusa ABS; *ABSPG* [filament:Prusa ABS @MK4 0.6] -inherits = Prusa ABS @MK4; *ABS06MK4* +inherits = Prusa ABS @MK4; *ABS06PG* [filament:Prusa ABS @MK4 0.8] -inherits = Prusa ABS @MK4; *ABS08MK4* +inherits = Prusa ABS @MK4; *ABS08PG* first_layer_temperature = 265 temperature = 265 @@ -9312,24 +10188,27 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 230 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Generic HIPS @PG] -inherits = Generic HIPS; *ABSPG* +[filament:Generic HIPS @XL] +inherits = Generic HIPS; *ABSPG*; *ABSXL* +renamed_from = "Generic HIPS @PG" bed_temperature = 105 -[filament:Generic HIPS @PG 0.6] -inherits = Generic HIPS @PG; *ABS06PG* +[filament:Generic HIPS @XL 0.6] +inherits = Generic HIPS @XL; *ABS06XL* +renamed_from = "Generic HIPS @PG 0.6" -[filament:Generic HIPS @PG 0.8] -inherits = Generic HIPS @PG; *ABS08PG* +[filament:Generic HIPS @XL 0.8] +inherits = Generic HIPS @XL; *ABS08XL* +renamed_from = "Generic HIPS @PG 0.8" [filament:Generic HIPS @MK4] -inherits = Generic HIPS; *ABSMK4* +inherits = Generic HIPS; *ABSPG* [filament:Generic HIPS @MK4 0.6] -inherits = Generic HIPS @MK4; *ABS06MK4* +inherits = Generic HIPS @MK4; *ABS06PG* [filament:Generic HIPS @MK4 0.8] -inherits = Generic HIPS @MK4; *ABS08MK4* +inherits = Generic HIPS @MK4; *ABS08PG* [filament:Prusa PETG] inherits = *PET* @@ -9342,16 +10221,25 @@ compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!= [filament:Prusa PETG @PG] inherits = Prusa PETG; *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PETG @PG 0.6] inherits = Prusa PETG; *PET06PG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusa PETG @PG 0.8] inherits = Prusa PETG; *PET08PG* temperature = 250 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Prusa PETG @XL] +inherits = Prusa PETG @PG; *PETXL* + +[filament:Prusa PETG @XL 0.6] +inherits = Prusa PETG @PG 0.6; *PET06XL* + +[filament:Prusa PETG @XL 0.8] +inherits = Prusa PETG @PG 0.8; *PET08XL* [filament:Prusa PETG @PGIS] inherits = Generic PETG @PG @@ -9365,7 +10253,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PETG @PGIS 0.6] inherits = Generic PETG @PG 0.6 @@ -9375,7 +10263,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusa PETG @PGIS 0.8] inherits = Generic PETG @PG 0.8 @@ -9385,7 +10273,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Verbatim PETG] inherits = *PET* @@ -9404,6 +10292,15 @@ inherits = Verbatim PETG; *PET06PG* [filament:Verbatim PETG @PG 0.8] inherits = Verbatim PETG; *PET08PG* +[filament:Verbatim PETG @XL] +inherits = Verbatim PETG @PG; *PETXL* + +[filament:Verbatim PETG @XL 0.6] +inherits = Verbatim PETG @PG 0.6; *PET06XL* + +[filament:Verbatim PETG @XL 0.8] +inherits = Verbatim PETG @PG 0.8; *PET08XL* + [filament:Prusament PETG] inherits = *PET* filament_vendor = Prusa Polymers @@ -9417,17 +10314,26 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!= [filament:Prusament PETG @PG] inherits = Prusament PETG; *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PETG @PG 0.6] inherits = Prusament PETG; *PET06PG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusament PETG @PG 0.8] inherits = Prusament PETG; *PET08PG* first_layer_temperature = 250 temperature = 260 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Prusament PETG @XL] +inherits = Prusament PETG @PG; *PETXL* + +[filament:Prusament PETG @XL 0.6] +inherits = Prusament PETG @PG 0.6; *PET06XL* + +[filament:Prusament PETG @XL 0.8] +inherits = Prusament PETG @PG 0.8; *PET08XL* [filament:Prusament PETG @PGIS] inherits = Prusament PETG @PG @@ -9439,7 +10345,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PETG @PGIS 0.6] inherits = Prusament PETG @PG 0.6 @@ -9447,7 +10353,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusament PETG @PGIS 0.8] inherits = Prusament PETG @PG 0.8 @@ -9455,7 +10361,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Prusament PETG V0 certified] inherits = Prusament PETG @@ -9483,6 +10389,15 @@ inherits = Prusament PETG V0 @PG; *PET06PG* [filament:Prusament PETG V0 @PG 0.8] inherits = Prusament PETG V0 @PG; *PET08PG* +[filament:Prusament PETG V0 @XL] +inherits = Prusament PETG V0 @PG; *PETXL* + +[filament:Prusament PETG V0 @XL 0.6] +inherits = Prusament PETG V0 @PG 0.6; *PET06XL* + +[filament:Prusament PETG V0 @XL 0.8] +inherits = Prusament PETG V0 @PG 0.8; *PET08XL* + [filament:Prusament PETG V0 @MINI] inherits = Prusament PETG V0 certified; *PETMINI* filament_notes = "" @@ -9507,6 +10422,15 @@ inherits = Prusament PETG Carbon Fiber @PG; *PET06PG* [filament:Prusament PETG Carbon Fiber @PG 0.8] inherits = Prusament PETG Carbon Fiber @PG; *PET08PG* +[filament:Prusament PETG Carbon Fiber @XL] +inherits = Prusament PETG Carbon Fiber @PG; *PETXL*; *04PLUSXL* + +[filament:Prusament PETG Carbon Fiber @XL 0.6] +inherits = Prusament PETG Carbon Fiber @PG 0.6; *PET06XL* + +[filament:Prusament PETG Carbon Fiber @XL 0.8] +inherits = Prusament PETG Carbon Fiber @PG 0.8; *PET08XL* + ## [filament:Prusament PETG Tungsten 75%] ## inherits = *PET* ## filament_vendor = Prusa Polymers @@ -9711,16 +10635,25 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG [filament:Prusa PLA @PG] inherits = Prusa PLA; *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PLA @PG 0.6] inherits = Prusa PLA; *PLA06PG* filament_max_volumetric_speed = 15.5 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusa PLA @PG 0.8] inherits = Prusa PLA; *PLA08PG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Prusa PLA @XL] +inherits = Prusa PLA @PG; *PLAXL* + +[filament:Prusa PLA @XL 0.6] +inherits = Prusa PLA @PG 0.6; *PLA06XL* + +[filament:Prusa PLA @XL 0.8] +inherits = Prusa PLA @PG 0.8; *PLA08XL* [filament:Prusa PLA @PGIS] inherits = Prusa PLA @PG @@ -9728,19 +10661,19 @@ renamed_from = "Prusa PLA @MK4IS" first_layer_temperature = 230 temperature = 220 slowdown_below_layer_time = 6 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PLA @PGIS 0.6] inherits = Prusa PLA @PG 0.6 first_layer_temperature = 230 temperature = 220 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusa PLA @PGIS 0.8] inherits = Prusa PLA @PG 0.8 first_layer_temperature = 230 temperature = 230 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Eolas Prints PLA] @@ -9762,6 +10695,15 @@ inherits = Eolas Prints PLA; *PLA06PG* [filament:Eolas Prints PLA @PG 0.8] inherits = Eolas Prints PLA; *PLA08PG* +[filament:Eolas Prints PLA @XL] +inherits = Eolas Prints PLA @PG; *PLAXL* + +[filament:Eolas Prints PLA @XL 0.6] +inherits = Eolas Prints PLA @PG 0.6; *PLA06XL* + +[filament:Eolas Prints PLA @XL 0.8] +inherits = Eolas Prints PLA @PG 0.8; *PLA08XL* + [filament:Eolas Prints PLA Matte] inherits = Eolas Prints PLA filament_cost = 25.50 @@ -9777,6 +10719,15 @@ inherits = Eolas Prints PLA Matte; *PLA06PG* [filament:Eolas Prints PLA Matte @PG 0.8] inherits = Eolas Prints PLA Matte; *PLA08PG* +[filament:Eolas Prints PLA Matte @XL] +inherits = Eolas Prints PLA Matte @PG; *PLAXL* + +[filament:Eolas Prints PLA Matte @XL 0.6] +inherits = Eolas Prints PLA Matte @PG 0.6; *PLA06XL* + +[filament:Eolas Prints PLA Matte @XL 0.8] +inherits = Eolas Prints PLA Matte @PG 0.8; *PLA08XL* + [filament:Eolas Prints INGEO 850] inherits = Eolas Prints PLA filament_cost = 25.90 @@ -9791,6 +10742,15 @@ inherits = Eolas Prints INGEO 850; *PLA06PG* [filament:Eolas Prints INGEO 850 @PG 0.8] inherits = Eolas Prints INGEO 850; *PLA08PG* +[filament:Eolas Prints INGEO 850 @XL] +inherits = Eolas Prints INGEO 850 @PG; *PLAXL* + +[filament:Eolas Prints INGEO 850 @XL 0.6] +inherits = Eolas Prints INGEO 850 @PG 0.6; *PLA06XL* + +[filament:Eolas Prints INGEO 850 @XL 0.8] +inherits = Eolas Prints INGEO 850 @PG 0.8; *PLA08XL* + [filament:Eolas Prints INGEO 870] inherits = Eolas Prints PLA filament_cost = 25.90 @@ -9808,6 +10768,15 @@ inherits = Eolas Prints INGEO 870; *PLA06PG* [filament:Eolas Prints INGEO 870 @PG 0.8] inherits = Eolas Prints INGEO 870; *PLA08PG* +[filament:Eolas Prints INGEO 870 @XL] +inherits = Eolas Prints INGEO 870 @PG; *PLAXL* + +[filament:Eolas Prints INGEO 870 @XL 0.6] +inherits = Eolas Prints INGEO 870 @PG 0.6; *PLA06XL* + +[filament:Eolas Prints INGEO 870 @XL 0.8] +inherits = Eolas Prints INGEO 870 @PG 0.8; *PLA08XL* + [filament:Eolas Prints PETG] inherits = *PET* filament_vendor = Eolas Prints @@ -9832,6 +10801,15 @@ inherits = Eolas Prints PETG; *PET06PG* [filament:Eolas Prints PETG @PG 0.8] inherits = Eolas Prints PETG; *PET08PG* +[filament:Eolas Prints PETG @XL] +inherits = Eolas Prints PETG @PG; *PETXL* + +[filament:Eolas Prints PETG @XL 0.6] +inherits = Eolas Prints PETG @PG 0.6; *PET06XL* + +[filament:Eolas Prints PETG @XL 0.8] +inherits = Eolas Prints PETG @PG 0.8; *PET08XL* + [filament:Eolas Prints PETG @MINI] inherits = Eolas Prints PETG; *PETMINI* @@ -9850,6 +10828,15 @@ inherits = Eolas Prints PETG - UV Resistant; *PET06PG* [filament:Eolas Prints PETG - UV Resistant @PG 0.8] inherits = Eolas Prints PETG - UV Resistant; *PET08PG* +[filament:Eolas Prints PETG - UV Resistant @XL] +inherits = Eolas Prints PETG - UV Resistant @PG; *PETXL* + +[filament:Eolas Prints PETG - UV Resistant @XL 0.6] +inherits = Eolas Prints PETG - UV Resistant @PG 0.6; *PET06XL* + +[filament:Eolas Prints PETG - UV Resistant @XL 0.8] +inherits = Eolas Prints PETG - UV Resistant @PG 0.8; *PET08XL* + [filament:Eolas Prints PETG - UV Resistant @MINI] inherits = Eolas Prints PETG - UV Resistant; *PETMINI* @@ -9866,27 +10853,31 @@ bed_temperature = 30 filament_retract_length = 0 extrusion_multiplier = 1.16 -[filament:Eolas Prints TPU 93A @PG] +[filament:Eolas Prints TPU 93A @XL] +inherits = Eolas Prints TPU 93A; *FLEXXL* +renamed_from = "Eolas Prints TPU 93A @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 +filament_retract_length_toolchange = 2.5 + +[filament:Eolas Prints TPU 93A @XL 0.6] +inherits = Eolas Prints TPU 93A @XL; *FLEX06XL* +renamed_from = "Eolas Prints TPU 93A @PG 0.6" + +[filament:Eolas Prints TPU 93A @PG 0.8] +inherits = Eolas Prints TPU 93A @XL; *FLEX08XL* +renamed_from = "Eolas Prints TPU 93A @PG 0.8" + +[filament:Eolas Prints TPU 93A @MK4] inherits = Eolas Prints TPU 93A; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 2.5 -[filament:Eolas Prints TPU 93A @PG 0.6] -inherits = Eolas Prints TPU 93A @PG; *FLEX06PG* - -[filament:Eolas Prints TPU 93A @PG 0.8] -inherits = Eolas Prints TPU 93A @PG; *FLEX08PG* - -[filament:Eolas Prints TPU 93A @MK4] -inherits = Eolas Prints TPU 93A; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 2.5 - [filament:Eolas Prints TPU 93A @MK4 0.6] -inherits = Eolas Prints TPU 93A @MK4; *FLEX06MK4* +inherits = Eolas Prints TPU 93A @MK4; *FLEX06PG* [filament:Eolas Prints TPU 93A @MK4 0.8] -inherits = Eolas Prints TPU 93A @MK4; *FLEX08MK4* +inherits = Eolas Prints TPU 93A @MK4; *FLEX08PG* [filament:Print With Smile PLA] inherits = *PLA* @@ -9910,6 +10901,17 @@ inherits = Print With Smile PLA; *PLA08PG* first_layer_temperature = 215 temperature = 215 +[filament:Print With Smile PLA @XL] +inherits = Print With Smile PLA @PG; *PLAXL* + +[filament:Print With Smile PLA @XL 0.6] +inherits = Print With Smile PLA @PG 0.6; *PLA06XL* + +[filament:Print With Smile PLA @XL 0.8] +inherits = Print With Smile PLA @PG 0.8; *PLA08XL* +first_layer_temperature = 215 +temperature = 215 + [filament:Print With Smile PETG] inherits = *PET* filament_vendor = Print With Smile @@ -9935,6 +10937,15 @@ filament_max_volumetric_speed = 16 inherits = Print With Smile PETG; *PET08PG* filament_max_volumetric_speed = 19 +[filament:Print With Smile PETG @XL] +inherits = Print With Smile PETG @PG; *PETXL* + +[filament:Print With Smile PETG @XL 0.6] +inherits = Print With Smile PETG @PG 0.6; *PET06XL* + +[filament:Print With Smile PETG @XL 0.8] +inherits = Print With Smile PETG @PG 0.8; *PET08XL* + [filament:Print With Smile PETG @MINI] inherits = Print With Smile PETG; *PETMINI* @@ -9955,27 +10966,30 @@ filament_max_volumetric_speed = 11 [filament:Print With Smile ASA @MINI] inherits = Print With Smile ASA; *ABSMINI* -[filament:Print With Smile ASA @PG] -inherits = Print With Smile ASA; *ABSPG* +[filament:Print With Smile ASA @XL] +inherits = Print With Smile ASA; *ABSPG*; *ABSXL* +renamed_from = "Print With Smile ASA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Print With Smile ASA @PG 0.6] -inherits = Print With Smile ASA @PG; *ABS06PG* +[filament:Print With Smile ASA @XL 0.6] +inherits = Print With Smile ASA @XL; *ABS06XL* +renamed_from = "Print With Smile ASA @PG 0.6" -[filament:Print With Smile ASA @PG 0.8] -inherits = Print With Smile ASA @PG; *ABS08PG* +[filament:Print With Smile ASA @XL 0.8] +inherits = Print With Smile ASA @XL; *ABS08XL* +renamed_from = "Print With Smile ASA @PG 0.8" first_layer_temperature = 255 temperature = 255 [filament:Print With Smile ASA @MK4] -inherits = Print With Smile ASA; *ABSMK4* +inherits = Print With Smile ASA; *ABSPG* [filament:Print With Smile ASA @MK4 0.6] -inherits = Print With Smile ASA @MK4; *ABS06MK4* +inherits = Print With Smile ASA @MK4; *ABS06PG* [filament:Print With Smile ASA @MK4 0.8] -inherits = Print With Smile ASA @MK4; *ABS08MK4* +inherits = Print With Smile ASA @MK4; *ABS08PG* first_layer_temperature = 255 temperature = 255 @@ -9990,25 +11004,28 @@ temperature = 240 [filament:Print With Smile ABS @MINI] inherits = Print With Smile ABS; *ABSMINI* -[filament:Print With Smile ABS @PG] -inherits = Print With Smile ABS; *ABSPG* +[filament:Print With Smile ABS @XL] +inherits = Print With Smile ABS; *ABSPG*; *ABSXL* +renamed_from = "Print With Smile ABS @PG" first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Print With Smile ABS @PG 0.6] -inherits = Print With Smile ABS @PG; *ABS06PG* +[filament:Print With Smile ABS @XL 0.6] +inherits = Print With Smile ABS @XL; *ABS06XL* +renamed_from = "Print With Smile ABS @PG 0.6" -[filament:Print With Smile ABS @PG 0.8] -inherits = Print With Smile ABS @PG; *ABS08PG* +[filament:Print With Smile ABS @XL 0.8] +inherits = Print With Smile ABS @XL; *ABS08XL* +renamed_from = "Print With Smile ABS @PG 0.8" [filament:Print With Smile ABS @MK4] -inherits = Print With Smile ABS; *ABSMK4* +inherits = Print With Smile ABS; *ABSPG* [filament:Print With Smile ABS @MK4 0.6] -inherits = Print With Smile ABS @MK4; *ABS06MK4* +inherits = Print With Smile ABS @MK4; *ABS06PG* [filament:Print With Smile ABS @MK4 0.8] -inherits = Print With Smile ABS @MK4; *ABS08MK4* +inherits = Print With Smile ABS @MK4; *ABS08PG* [filament:Print With Smile PETG CF] inherits = Extrudr XPETG CF @@ -10039,6 +11056,15 @@ filament_max_volumetric_speed = 6.5 inherits = Print With Smile PETG CF @PG; *PET08PG* filament_max_volumetric_speed = 8 +[filament:Print With Smile PETG CF @XL] +inherits = Print With Smile PETG CF @PG; *PETXL* + +[filament:Print With Smile PETG CF @XL 0.6] +inherits = Print With Smile PETG CF @PG 0.6; *PET06XL* + +[filament:Print With Smile PETG CF @XL 0.8] +inherits = Print With Smile PETG CF @PG 0.8; *PET08XL* + [filament:Print With Smile TPU96A] inherits = *FLEX* filament_vendor = Print With Smile @@ -10055,30 +11081,37 @@ full_fan_speed_layer = 6 filament_retract_length = 1.2 filament_deretract_speed = 20 -[filament:Print With Smile TPU96A @PG] -inherits = Print With Smile TPU96A; *FLEXPG* +[filament:Print With Smile TPU96A @XL] +inherits = Print With Smile TPU96A; *FLEXXL* +renamed_from = "Print With Smile TPU96A @PG" filament_retract_length = 2 filament_max_volumetric_speed = 2.5 +filament_multitool_ramming_flow = 2.5 +filament_retract_length_toolchange = 2 -[filament:Print With Smile TPU96A @PG 0.6] -inherits = Print With Smile TPU96A @PG; *FLEX06PG* +[filament:Print With Smile TPU96A @XL 0.6] +inherits = Print With Smile TPU96A @XL; *FLEX06XL* +renamed_from = "Print With Smile TPU96A @PG 0.6" filament_max_volumetric_speed = 3.5 +filament_multitool_ramming_flow = 3.5 -[filament:Print With Smile TPU96A @PG 0.8] -inherits = Print With Smile TPU96A @PG; *FLEX08PG* +[filament:Print With Smile TPU96A @XL 0.8] +inherits = Print With Smile TPU96A @XL; *FLEX08XL* +renamed_from = "Print With Smile TPU96A @PG 0.8" filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 [filament:Print With Smile TPU96A @MK4] -inherits = Print With Smile TPU96A; *FLEXMK4* +inherits = Print With Smile TPU96A; *FLEXPG* filament_retract_length = 2 filament_max_volumetric_speed = 3 [filament:Print With Smile TPU96A @MK4 0.6] -inherits = Print With Smile TPU96A @MK4; *FLEX06MK4* +inherits = Print With Smile TPU96A @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Print With Smile TPU96A @MK4 0.8] -inherits = Print With Smile TPU96A @MK4; *FLEX08MK4* +inherits = Print With Smile TPU96A @MK4; *FLEX08PG* filament_max_volumetric_speed = 8 [filament:Fiberlogy Easy PLA] @@ -10100,6 +11133,15 @@ inherits = Fiberlogy Easy PLA; *PLA06PG* [filament:Fiberlogy Easy PLA @PG 0.8] inherits = Fiberlogy Easy PLA; *PLA08PG* +[filament:Fiberlogy Easy PLA @XL] +inherits = Fiberlogy Easy PLA @PG; *PLAXL* + +[filament:Fiberlogy Easy PLA @XL 0.6] +inherits = Fiberlogy Easy PLA @PG 0.6; *PLA06XL* + +[filament:Fiberlogy Easy PLA @XL 0.8] +inherits = Fiberlogy Easy PLA @PG 0.8; *PLA08XL* + [filament:Fiberlogy Easy PET-G] inherits = *PET* renamed_from = Fiberlogy PETG @@ -10128,6 +11170,15 @@ inherits = Fiberlogy Easy PET-G; *PET06PG* [filament:Fiberlogy Easy PET-G @PG 0.8] inherits = Fiberlogy Easy PET-G; *PET08PG* +[filament:Fiberlogy Easy PET-G @XL] +inherits = Fiberlogy Easy PET-G @PG; *PETXL* + +[filament:Fiberlogy Easy PET-G @XL 0.6] +inherits = Fiberlogy Easy PET-G @PG 0.6; *PET06XL* + +[filament:Fiberlogy Easy PET-G @XL 0.8] +inherits = Fiberlogy Easy PET-G @PG 0.8; *PET08XL* + [filament:Fiberlogy ASA] inherits = *ABS* filament_vendor = Fiberlogy @@ -10149,25 +11200,28 @@ filament_type = ASA fan_below_layer_time = 30 disable_fan_first_layers = 5 -[filament:Fiberlogy ASA @PG] -inherits = Fiberlogy ASA; *ABSPG* +[filament:Fiberlogy ASA @XL] +inherits = Fiberlogy ASA; *ABSPG*; *ABSXL* +renamed_from = "Fiberlogy ASA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Fiberlogy ASA @PG 0.6] -inherits = Fiberlogy ASA @PG; *ABS06PG* +[filament:Fiberlogy ASA @XL 0.6] +inherits = Fiberlogy ASA @XL; *ABS06XL* +renamed_from = "Fiberlogy ASA @PG 0.6" -[filament:Fiberlogy ASA @PG 0.8] -inherits = Fiberlogy ASA @PG; *ABS08PG* +[filament:Fiberlogy ASA @XL 0.8] +inherits = Fiberlogy ASA @XL; *ABS08XL* +renamed_from = "Fiberlogy ASA @PG 0.8" [filament:Fiberlogy ASA @MK4] -inherits = Fiberlogy ASA; *ABSMK4* +inherits = Fiberlogy ASA; *ABSPG* [filament:Fiberlogy ASA @MK4 0.6] -inherits = Fiberlogy ASA @MK4; *ABS06MK4* +inherits = Fiberlogy ASA @MK4; *ABS06PG* [filament:Fiberlogy ASA @MK4 0.8] -inherits = Fiberlogy ASA @MK4; *ABS08MK4* +inherits = Fiberlogy ASA @MK4; *ABS08PG* [filament:Fiberlogy ASA @MINI] inherits = Fiberlogy ASA; *ABSMINI* @@ -10192,15 +11246,21 @@ disable_fan_first_layers = 5 [filament:Fiberlogy Easy ABS @PG] inherits = Fiberlogy Easy ABS; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @PG 0.6] inherits = Fiberlogy Easy ABS; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @PG 0.8] inherits = Fiberlogy Easy ABS; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberlogy Easy ABS @XL] +inherits = Fiberlogy Easy ABS @PG; *ABSXL* + +[filament:Fiberlogy Easy ABS @XL 0.6] +inherits = Fiberlogy Easy ABS @PG 0.6; *ABS06XL* + +[filament:Fiberlogy Easy ABS @XL 0.8] +inherits = Fiberlogy Easy ABS @PG 0.8; *ABS08XL* [filament:Fiberlogy Easy ABS @MINI] inherits = Fiberlogy Easy ABS; *ABSMINI* @@ -10238,6 +11298,15 @@ inherits = Fiberlogy CPE HT @PG; *PET06PG* [filament:Fiberlogy CPE HT @PG 0.8] inherits = Fiberlogy CPE HT @PG; *PET08PG* +[filament:Fiberlogy CPE HT @XL] +inherits = Fiberlogy CPE HT @PG; *PETXL* + +[filament:Fiberlogy CPE HT @XL 0.6] +inherits = Fiberlogy CPE HT @PG 0.6; *PET06XL* + +[filament:Fiberlogy CPE HT @XL 0.8] +inherits = Fiberlogy CPE HT @PG 0.8; *PET08XL* + [filament:Fiberlogy PCTG] inherits = Fiberlogy CPE HT filament_cost = 29.41 @@ -10260,6 +11329,15 @@ inherits = Fiberlogy PCTG; *PET06PG* [filament:Fiberlogy PCTG @PG 0.8] inherits = Fiberlogy PCTG; *PET08PG* +[filament:Fiberlogy PCTG @XL] +inherits = Fiberlogy PCTG @PG; *PETXL* + +[filament:Fiberlogy PCTG @XL 0.6] +inherits = Fiberlogy PCTG @PG 0.6; *PET06XL* + +[filament:Fiberlogy PCTG @XL 0.8] +inherits = Fiberlogy PCTG @PG 0.8; *PET08XL* + [filament:Fiberlogy PCTG @MINI] inherits = Fiberlogy PCTG; *PETMINI* @@ -10290,24 +11368,31 @@ min_print_speed = 15 cooling = 1 filament_spool_weight = 330 -[filament:Fiberlogy FiberFlex 40D @PG] -inherits = Fiberlogy FiberFlex 40D; *FLEXPG* +[filament:Fiberlogy FiberFlex 40D @XL] +inherits = Fiberlogy FiberFlex 40D; *FLEXXL* +renamed_from = "Fiberlogy FiberFlex 40D @PG" filament_max_volumetric_speed = 3 filament_retract_length = 2.2 extrusion_multiplier = 1.1 first_layer_temperature = 220 temperature = 220 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 2.2 -[filament:Fiberlogy FiberFlex 40D @PG 0.6] -inherits = Fiberlogy FiberFlex 40D @PG; *FLEX06PG* +[filament:Fiberlogy FiberFlex 40D @XL 0.6] +inherits = Fiberlogy FiberFlex 40D @XL; *FLEX06XL* +renamed_from = "Fiberlogy FiberFlex 40D @PG 0.6" filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:Fiberlogy FiberFlex 40D @PG 0.8] -inherits = Fiberlogy FiberFlex 40D @PG; *FLEX08PG* +[filament:Fiberlogy FiberFlex 40D @XL 0.8] +inherits = Fiberlogy FiberFlex 40D @XL; *FLEX08XL* +renamed_from = "Fiberlogy FiberFlex 40D @PG 0.8" filament_max_volumetric_speed = 9 +filament_multitool_ramming_flow = 9 [filament:Fiberlogy FiberFlex 40D @MK4] -inherits = Fiberlogy FiberFlex 40D; *FLEXMK4* +inherits = Fiberlogy FiberFlex 40D; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 2.2 extrusion_multiplier = 1.1 @@ -10315,11 +11400,11 @@ first_layer_temperature = 220 temperature = 220 [filament:Fiberlogy FiberFlex 40D @MK4 0.6] -inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX06MK4* +inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Fiberlogy FiberFlex 40D @MK4 0.8] -inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX08MK4* +inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberFlex 40D @MINI] @@ -10351,30 +11436,37 @@ filament_retract_before_travel = 2 filament_cost = 49.11 filament_retract_length = 1.2 -[filament:Fiberlogy MattFlex 40D @PG] -inherits = Fiberlogy MattFlex 40D; *FLEXPG* +[filament:Fiberlogy MattFlex 40D @XL] +inherits = Fiberlogy MattFlex 40D; *FLEXXL* +renamed_from = "Fiberlogy MattFlex 40D @PG" filament_max_volumetric_speed = 3 filament_retract_length = 2.2 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 2.2 -[filament:Fiberlogy MattFlex 40D @PG 0.6] -inherits = Fiberlogy MattFlex 40D @PG; *FLEX06PG* +[filament:Fiberlogy MattFlex 40D @XL 0.6] +inherits = Fiberlogy MattFlex 40D @XL; *FLEX06XL* +renamed_from = "Fiberlogy MattFlex 40D @PG 0.6" filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:Fiberlogy MattFlex 40D @PG 0.8] -inherits = Fiberlogy MattFlex 40D @PG; *FLEX08PG* +[filament:Fiberlogy MattFlex 40D @XL 0.8] +inherits = Fiberlogy MattFlex 40D @XL; *FLEX08XL* +renamed_from = "Fiberlogy MattFlex 40D @PG 0.8" filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fiberlogy MattFlex 40D @MK4] -inherits = Fiberlogy MattFlex 40D; *FLEXMK4* +inherits = Fiberlogy MattFlex 40D; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 2.2 [filament:Fiberlogy MattFlex 40D @MK4 0.6] -inherits = Fiberlogy MattFlex 40D @MK4; *FLEX06MK4* +inherits = Fiberlogy MattFlex 40D @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Fiberlogy MattFlex 40D @MK4 0.8] -inherits = Fiberlogy MattFlex 40D @MK4; *FLEX08MK4* +inherits = Fiberlogy MattFlex 40D @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberFlex 30D] @@ -10388,8 +11480,9 @@ max_fan_speed = 60 filament_density = 1.07 filament_retract_length = 1.2 -[filament:Fiberlogy FiberFlex 30D @PG] -inherits = Fiberlogy FiberFlex 30D; *FLEXPG* +[filament:Fiberlogy FiberFlex 30D @XL] +inherits = Fiberlogy FiberFlex 30D; *FLEXXL* +renamed_from = "Fiberlogy FiberFlex 30D @PG" filament_max_volumetric_speed = 2.5 filament_retract_length = 3 first_layer_temperature = 220 @@ -10397,17 +11490,22 @@ temperature = 220 first_layer_bed_temperature = 55 bed_temperature = 55 extrusion_multiplier = 1.1 +filament_retract_length_toolchange = 3 -[filament:Fiberlogy FiberFlex 30D @PG 0.6] -inherits = Fiberlogy FiberFlex 30D @PG; *FLEX06PG* +[filament:Fiberlogy FiberFlex 30D @XL 0.6] +inherits = Fiberlogy FiberFlex 30D @XL; *FLEX06XL* +renamed_from = "Fiberlogy FiberFlex 30D @PG 0.6" filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 -[filament:Fiberlogy FiberFlex 30D @PG 0.8] -inherits = Fiberlogy FiberFlex 30D @PG; *FLEX08PG* +[filament:Fiberlogy FiberFlex 30D @XL 0.8] +inherits = Fiberlogy FiberFlex 30D @XL; *FLEX08XL* +renamed_from = "Fiberlogy FiberFlex 30D @PG 0.8" filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fiberlogy FiberFlex 30D @MK4] -inherits = Fiberlogy FiberFlex 30D; *FLEXMK4* +inherits = Fiberlogy FiberFlex 30D; *FLEXPG* filament_max_volumetric_speed = 3.5 filament_retract_length = 3 first_layer_temperature = 220 @@ -10417,11 +11515,11 @@ bed_temperature = 55 extrusion_multiplier = 1.1 [filament:Fiberlogy FiberFlex 30D @MK4 0.6] -inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX06MK4* +inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:Fiberlogy FiberFlex 30D @MK4 0.8] -inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX08MK4* +inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberSatin] @@ -10441,6 +11539,15 @@ inherits = Fiberlogy FiberSatin; *PLA06PG* [filament:Fiberlogy FiberSatin @PG 0.8] inherits = Fiberlogy FiberSatin; *PLA08PG* +[filament:Fiberlogy FiberSatin @XL] +inherits = Fiberlogy FiberSatin @PG; *PLAXL* + +[filament:Fiberlogy FiberSatin @XL 0.6] +inherits = Fiberlogy FiberSatin @PG 0.6; *PLA06XL* + +[filament:Fiberlogy FiberSatin @XL 0.8] +inherits = Fiberlogy FiberSatin @PG 0.8; *PLA08XL* + [filament:Fiberlogy FiberSilk] inherits = Fiberlogy FiberSatin first_layer_temperature = 230 @@ -10458,6 +11565,15 @@ inherits = Fiberlogy FiberSilk; *PLA06PG* [filament:Fiberlogy FiberSilk @PG 0.8] inherits = Fiberlogy FiberSilk; *PLA08PG* +[filament:Fiberlogy FiberSilk @XL] +inherits = Fiberlogy FiberSilk @PG; *PLAXL* + +[filament:Fiberlogy FiberSilk @XL 0.6] +inherits = Fiberlogy FiberSilk @PG 0.6; *PLA06XL* + +[filament:Fiberlogy FiberSilk @XL 0.8] +inherits = Fiberlogy FiberSilk @PG 0.8; *PLA08XL* + [filament:Fiberlogy FiberWood] inherits = Fiberlogy Easy PLA first_layer_temperature = 185 @@ -10481,6 +11597,15 @@ filament_max_volumetric_speed = 15 first_layer_temperature = 195 temperature = 195 +[filament:Fiberlogy FiberWood @XL] +inherits = Fiberlogy FiberWood @PG; *PLAXL* + +[filament:Fiberlogy FiberWood @XL 0.6] +inherits = Fiberlogy FiberWood @PG 0.6; *PLA06XL* + +[filament:Fiberlogy FiberWood @XL 0.8] +inherits = Fiberlogy FiberWood @PG 0.8; *PLA08XL* + [filament:Fiberlogy HD PLA] inherits = Fiberlogy Easy PLA first_layer_temperature = 230 @@ -10498,6 +11623,15 @@ inherits = Fiberlogy HD PLA; *PLA06PG* [filament:Fiberlogy HD PLA @PG 0.8] inherits = Fiberlogy HD PLA; *PLA08PG* +[filament:Fiberlogy HD PLA @XL] +inherits = Fiberlogy HD PLA @PG; *PLAXL* + +[filament:Fiberlogy HD PLA @XL 0.6] +inherits = Fiberlogy HD PLA @PG 0.6; *PLA06XL* + +[filament:Fiberlogy HD PLA @XL 0.8] +inherits = Fiberlogy HD PLA @PG 0.8; *PLA08XL* + [filament:Fiberlogy PLA Mineral] inherits = Fiberlogy Easy PLA first_layer_temperature = 195 @@ -10521,6 +11655,15 @@ filament_max_volumetric_speed = 14 first_layer_temperature = 200 temperature = 200 +[filament:Fiberlogy PLA Mineral @XL] +inherits = Fiberlogy PLA Mineral @PG; *PLAXL* + +[filament:Fiberlogy PLA Mineral @XL 0.6] +inherits = Fiberlogy PLA Mineral @PG 0.6; *PLA06XL* + +[filament:Fiberlogy PLA Mineral @XL 0.8] +inherits = Fiberlogy PLA Mineral @PG 0.8; *PLA08XL* + [filament:Fiberlogy Impact PLA] inherits = Fiberlogy HD PLA filament_density = 1.22 @@ -10535,6 +11678,15 @@ inherits = Fiberlogy Impact PLA; *PLA06PG* [filament:Fiberlogy Impact PLA @PG 0.8] inherits = Fiberlogy Impact PLA; *PLA08PG* +[filament:Fiberlogy Impact PLA @XL] +inherits = Fiberlogy Impact PLA @PG; *PLAXL* + +[filament:Fiberlogy Impact PLA @XL 0.6] +inherits = Fiberlogy Impact PLA @PG 0.6; *PLA06XL* + +[filament:Fiberlogy Impact PLA @XL 0.8] +inherits = Fiberlogy Impact PLA @PG 0.8; *PLA08XL* + [filament:Fiberlogy Nylon PA12] inherits = Fiberlogy ASA filament_type = PA @@ -10553,30 +11705,33 @@ filament_retract_lift = 0.2 filament_max_volumetric_speed = 6 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K26{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -[filament:Fiberlogy Nylon PA12 @PG] -inherits = Fiberlogy Nylon PA12; *ABSPG* +[filament:Fiberlogy Nylon PA12 @XL] +inherits = Fiberlogy Nylon PA12; *ABSPG*; *ABSXL* +renamed_from = "Fiberlogy Nylon PA12 @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 6 -[filament:Fiberlogy Nylon PA12 @PG 0.6] -inherits = Fiberlogy Nylon PA12 @PG; *ABS06PG* +[filament:Fiberlogy Nylon PA12 @XL 0.6] +inherits = Fiberlogy Nylon PA12 @XL; *ABS06XL* +renamed_from = "Fiberlogy Nylon PA12 @PG 0.6" filament_max_volumetric_speed = 8 -[filament:Fiberlogy Nylon PA12 @PG 0.8] -inherits = Fiberlogy Nylon PA12 @PG; *ABS08PG* +[filament:Fiberlogy Nylon PA12 @XL 0.8] +inherits = Fiberlogy Nylon PA12 @XL; *ABS08XL* +renamed_from = "Fiberlogy Nylon PA12 @PG 0.8" filament_max_volumetric_speed = 11 [filament:Fiberlogy Nylon PA12 @MK4] -inherits = Fiberlogy Nylon PA12; *ABSMK4* +inherits = Fiberlogy Nylon PA12; *ABSPG* filament_max_volumetric_speed = 6 [filament:Fiberlogy Nylon PA12 @MK4 0.6] -inherits = Fiberlogy Nylon PA12 @MK4; *ABS06MK4* +inherits = Fiberlogy Nylon PA12 @MK4; *ABS06PG* filament_max_volumetric_speed = 8 [filament:Fiberlogy Nylon PA12 @MK4 0.8] -inherits = Fiberlogy Nylon PA12 @MK4; *ABS08MK4* +inherits = Fiberlogy Nylon PA12 @MK4; *ABS08PG* filament_max_volumetric_speed = 11 [filament:Fiberlogy Nylon PA12+CF15] @@ -10596,32 +11751,34 @@ fan_always_on = 0 filament_max_volumetric_speed = 8 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material -[filament:Fiberlogy Nylon PA12+CF15 @PG] -inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *04PLUSPG* +[filament:Fiberlogy Nylon PA12+CF15 @XL] +inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *ABSXL*; *04PLUSXL* +renamed_from = "Fiberlogy Nylon PA12+CF15 @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material -[filament:Fiberlogy Nylon PA12+CF15 @PG 0.6] -inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS06PG* +[filament:Fiberlogy Nylon PA12+CF15 @XL 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @XL; *ABS06XL* +renamed_from = "Fiberlogy Nylon PA12+CF15 @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Fiberlogy Nylon PA12+CF15 @PG 0.8] -inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS08PG* +[filament:Fiberlogy Nylon PA12+CF15 @XL 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @XL; *ABS08XL* +renamed_from = "Fiberlogy Nylon PA12+CF15 @PG 0.8" filament_max_volumetric_speed = 12 [filament:Fiberlogy Nylon PA12+CF15 @MK4] -inherits = Fiberlogy Nylon PA12+CF15; *ABSMK4*; *04PLUSPG* +inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *04PLUSPG* filament_max_volumetric_speed = 8 compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:Fiberlogy Nylon PA12+CF15 @MK4 0.6] -inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS06MK4* +inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy Nylon PA12+CF15 @MK4 0.8] -inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS08MK4* +inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Fiberlogy Nylon PA12+GF15] @@ -10629,16 +11786,19 @@ inherits = Fiberlogy Nylon PA12+CF15 filament_density = 1.13 filament_max_volumetric_speed = 8 -[filament:Fiberlogy Nylon PA12+GF15 @PG] -inherits = Fiberlogy Nylon PA12+CF15 @PG +[filament:Fiberlogy Nylon PA12+GF15 @XL] +inherits = Fiberlogy Nylon PA12+CF15 @XL +renamed_from = "Fiberlogy Nylon PA12+GF15 @PG" filament_density = 1.13 -[filament:Fiberlogy Nylon PA12+GF15 @PG 0.6] -inherits = Fiberlogy Nylon PA12+CF15 @PG 0.6 +[filament:Fiberlogy Nylon PA12+GF15 @XL 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @XL 0.6 +renamed_from = "Fiberlogy Nylon PA12+GF15 @PG 0.6" filament_density = 1.13 -[filament:Fiberlogy Nylon PA12+GF15 @PG 0.8] -inherits = Fiberlogy Nylon PA12+CF15 @PG 0.8 +[filament:Fiberlogy Nylon PA12+GF15 @XL 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @XL 0.8 +renamed_from = "Fiberlogy Nylon PA12+GF15 @PG 0.8" filament_density = 1.13 [filament:Fiberlogy Nylon PA12+GF15 @MK4] @@ -10679,19 +11839,25 @@ filament_max_volumetric_speed = 5 [filament:Fiberlogy PP @PG] inherits = Fiberlogy PP; *ABSPG* filament_max_volumetric_speed = 5 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy PP @PG 0.6] inherits = Fiberlogy PP @PG; *ABS06PG* filament_max_volumetric_speed = 7 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy PP @PG 0.8] inherits = Fiberlogy PP @PG; *ABS08PG* filament_max_volumetric_speed = 10 first_layer_temperature = 250 temperature = 250 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberlogy PP @XL] +inherits = Fiberlogy PP @PG; *ABSXL* + +[filament:Fiberlogy PP @XL 0.6] +inherits = Fiberlogy PP @PG 0.6; *ABS06XL* + +[filament:Fiberlogy PP @XL 0.8] +inherits = Fiberlogy PP @PG 0.8; *ABS08XL* [filament:Filament PM PLA] inherits = *PLA* @@ -10713,6 +11879,15 @@ inherits = Filament PM PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Filament PM PLA @XL] +inherits = Filament PM PLA @PG; *PLAXL* + +[filament:Filament PM PLA @XL 0.6] +inherits = Filament PM PLA @PG 0.6; *PLA06XL* + +[filament:Filament PM PLA @XL 0.8] +inherits = Filament PM PLA @PG 0.8; *PLA08XL* + [filament:AmazonBasics PLA] inherits = *PLA* filament_vendor = AmazonBasics @@ -10730,6 +11905,15 @@ inherits = AmazonBasics PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:AmazonBasics PLA @XL] +inherits = AmazonBasics PLA @PG; *PLAXL* + +[filament:AmazonBasics PLA @XL 0.6] +inherits = AmazonBasics PLA @PG 0.6; *PLA06XL* + +[filament:AmazonBasics PLA @XL 0.8] +inherits = AmazonBasics PLA @PG 0.8; *PLA08XL* + [filament:Overture PLA] inherits = *PLA* filament_vendor = Overture @@ -10748,6 +11932,15 @@ inherits = Overture PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Overture PLA @XL] +inherits = Overture PLA @PG; *PLAXL* + +[filament:Overture PLA @XL 0.6] +inherits = Overture PLA @PG 0.6; *PLA06XL* + +[filament:Overture PLA @XL 0.8] +inherits = Overture PLA @PG 0.8; *PLA08XL* + [filament:Hatchbox PLA] inherits = *PLA* filament_vendor = Hatchbox @@ -10770,6 +11963,15 @@ inherits = Hatchbox PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Hatchbox PLA @XL] +inherits = Hatchbox PLA @PG; *PLAXL* + +[filament:Hatchbox PLA @XL 0.6] +inherits = Hatchbox PLA @PG 0.6; *PLA06XL* + +[filament:Hatchbox PLA @XL 0.8] +inherits = Hatchbox PLA @PG 0.8; *PLA08XL* + [filament:Esun PLA] inherits = *PLA* filament_vendor = Esun @@ -10790,6 +11992,15 @@ inherits = Esun PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Esun PLA @XL] +inherits = Esun PLA @PG; *PLAXL* + +[filament:Esun PLA @XL 0.6] +inherits = Esun PLA @PG 0.6; *PLA06XL* + +[filament:Esun PLA @XL 0.8] +inherits = Esun PLA @PG 0.8; *PLA08XL* + [filament:Das Filament PLA] inherits = *PLA* filament_vendor = Das Filament @@ -10807,6 +12018,15 @@ inherits = Das Filament PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Das Filament PLA @XL] +inherits = Das Filament PLA @PG; *PLAXL* + +[filament:Das Filament PLA @XL 0.6] +inherits = Das Filament PLA @PG 0.6; *PLA06XL* + +[filament:Das Filament PLA @XL 0.8] +inherits = Das Filament PLA @PG 0.8; *PLA08XL* + [filament:EUMAKERS PLA] inherits = *PLA* filament_vendor = EUMAKERS @@ -10825,6 +12045,15 @@ inherits = EUMAKERS PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:EUMAKERS PLA @XL] +inherits = EUMAKERS PLA @PG; *PLAXL* + +[filament:EUMAKERS PLA @XL 0.6] +inherits = EUMAKERS PLA @PG 0.6; *PLA06XL* + +[filament:EUMAKERS PLA @XL 0.8] +inherits = EUMAKERS PLA @PG 0.8; *PLA08XL* + [filament:Floreon3D PLA] inherits = *PLA* filament_vendor = Floreon3D @@ -10842,6 +12071,15 @@ inherits = Floreon3D PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Floreon3D PLA @XL] +inherits = Floreon3D PLA @PG; *PLAXL* + +[filament:Floreon3D PLA @XL 0.6] +inherits = Floreon3D PLA @PG 0.6; *PLA06XL* + +[filament:Floreon3D PLA @XL 0.8] +inherits = Floreon3D PLA @PG 0.8; *PLA08XL* + [filament:Prusament PLA] inherits = *PLA* filament_vendor = Prusa Polymers @@ -10854,31 +12092,40 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG [filament:Prusament PLA @PG] inherits = Prusament PLA; *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PLA @PG 0.6] inherits = Prusament PLA; *PLA06PG* filament_max_volumetric_speed = 16 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusament PLA @PG 0.8] inherits = Prusament PLA; *PLA08PG* first_layer_temperature = 230 temperature = 225 +[filament:Prusament PLA @XL] +inherits = Prusament PLA @PG; *PLAXL* + +[filament:Prusament PLA @XL 0.6] +inherits = Prusament PLA @PG 0.6; *PLA06XL* + +[filament:Prusament PLA @XL 0.8] +inherits = Prusament PLA @PG 0.8; *PLA08XL* + [filament:Prusament PLA @PGIS] inherits = Prusament PLA @PG renamed_from = "Prusament PLA @MK4IS" first_layer_temperature = 230 temperature = 225 slowdown_below_layer_time = 6 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PLA @PGIS 0.6] inherits = Prusament PLA @PG 0.6 first_layer_temperature = 230 temperature = 225 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusament PLA Blend @PGIS] inherits = Prusament PLA @PG @@ -10888,20 +12135,20 @@ temperature = 225 filament_max_volumetric_speed = 7 slowdown_below_layer_time = 7 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.035{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PLA Blend @PGIS 0.6] inherits = Prusament PLA Blend @PGIS filament_max_volumetric_speed = 9 slowdown_below_layer_time = 14 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusament PLA Blend @PGIS 0.8] inherits = Prusament PLA Blend @PGIS filament_max_volumetric_speed = 12 slowdown_below_layer_time = 20 temperature = 230 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Prusament PVB] inherits = *PLA* @@ -10932,6 +12179,15 @@ inherits = Prusament PVB @PG; *PLA08PG* first_layer_temperature = 225 temperature = 225 +[filament:Prusament PVB @XL] +inherits = Prusament PVB @PG; *PLAXL* + +[filament:Prusament PVB @XL 0.6] +inherits = Prusament PVB @PG 0.6; *PLA06XL* + +[filament:Prusament PVB @XL 0.8] +inherits = Prusament PVB @PG 0.8; *PLA08XL* + [filament:*PLA MMU2*] inherits = Prusa PLA compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.25 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material @@ -11035,32 +12291,39 @@ full_fan_speed_layer = 6 filament_retract_length = 1.2 filament_deretract_speed = 20 -[filament:Fillamentum Flexfill 98A @PG] -inherits = Fillamentum Flexfill 98A; *FLEXPG* +[filament:Fillamentum Flexfill 98A @XL] +inherits = Fillamentum Flexfill 98A; *FLEXXL* +renamed_from = "Fillamentum Flexfill 98A @PG" filament_max_volumetric_speed = 2.5 filament_retract_length = 3 extrusion_multiplier = 1.08 +filament_multitool_ramming_flow = 2.5 +filament_retract_length_toolchange = 3 -[filament:Fillamentum Flexfill 98A @PG 0.6] -inherits = Fillamentum Flexfill 98A @PG; *FLEX06PG* +[filament:Fillamentum Flexfill 98A @XL 0.6] +inherits = Fillamentum Flexfill 98A @XL; *FLEX06XL* +renamed_from = "Fillamentum Flexfill 98A @PG 0.6" filament_max_volumetric_speed = 3 +filament_multitool_ramming_flow = 3 -[filament:Fillamentum Flexfill 98A @PG 0.8] -inherits = Fillamentum Flexfill 98A @PG; *FLEX08PG* +[filament:Fillamentum Flexfill 98A @XL 0.8] +inherits = Fillamentum Flexfill 98A @XL; *FLEX08XL* +renamed_from = "Fillamentum Flexfill 98A @PG 0.8" filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fillamentum Flexfill 98A @MK4] -inherits = Fillamentum Flexfill 98A; *FLEXMK4* +inherits = Fillamentum Flexfill 98A; *FLEXPG* filament_max_volumetric_speed = 3 filament_retract_length = 3 extrusion_multiplier = 1.08 [filament:Fillamentum Flexfill 98A @MK4 0.6] -inherits = Fillamentum Flexfill 98A @MK4; *FLEX06MK4* +inherits = Fillamentum Flexfill 98A @MK4; *FLEX06PG* filament_max_volumetric_speed = 4 [filament:Fillamentum Flexfill 98A @MK4 0.8] -inherits = Fillamentum Flexfill 98A @MK4; *FLEX08MK4* +inherits = Fillamentum Flexfill 98A @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:ColorFabb VarioShore TPU] @@ -11074,12 +12337,15 @@ extrusion_multiplier = 0.85 first_layer_temperature = 220 temperature = 220 -[filament:ColorFabb VarioShore TPU @PG] -inherits = ColorFabb VarioShore TPU; *FLEXPG* +[filament:ColorFabb VarioShore TPU @XL] +inherits = ColorFabb VarioShore TPU; *FLEXXL* +renamed_from = "ColorFabb VarioShore TPU @PG" filament_max_volumetric_speed = 1.5 +filament_multitool_ramming_flow = 1.5 +filament_retract_length_toolchange = nil [filament:ColorFabb VarioShore TPU @MK4] -inherits = ColorFabb VarioShore TPU; *FLEXMK4* +inherits = ColorFabb VarioShore TPU; *FLEXPG* filament_max_volumetric_speed = 1.5 [filament:Taulman Bridge] @@ -11105,31 +12371,34 @@ min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Taulman Bridge @PG] -inherits = Taulman Bridge; *ABSPG* +[filament:Taulman Bridge @XL] +inherits = Taulman Bridge; *ABSPG*; *ABSXL* +renamed_from = "Taulman Bridge @PG" bed_temperature = 105 filament_max_volumetric_speed = 7 -[filament:Taulman Bridge @PG 0.6] -inherits = Taulman Bridge @PG; *ABS06PG* +[filament:Taulman Bridge @XL 0.6] +inherits = Taulman Bridge @XL; *ABS06XL* +renamed_from = "Taulman Bridge @PG 0.6" filament_max_volumetric_speed = 9 -[filament:Taulman Bridge @PG 0.8] -inherits = Taulman Bridge @PG; *ABS08PG* +[filament:Taulman Bridge @XL 0.8] +inherits = Taulman Bridge @XL; *ABS08XL* +renamed_from = "Taulman Bridge @PG 0.8" filament_max_volumetric_speed = 12 first_layer_temperature = 270 temperature = 270 [filament:Taulman Bridge @MK4] -inherits = Taulman Bridge; *ABSMK4* +inherits = Taulman Bridge; *ABSPG* filament_max_volumetric_speed = 7 [filament:Taulman Bridge @MK4 0.6] -inherits = Taulman Bridge @MK4; *ABS06MK4* +inherits = Taulman Bridge @MK4; *ABS06PG* filament_max_volumetric_speed = 9 [filament:Taulman Bridge @MK4 0.8] -inherits = Taulman Bridge @MK4; *ABS08MK4* +inherits = Taulman Bridge @MK4; *ABS08PG* filament_max_volumetric_speed = 12 first_layer_temperature = 270 temperature = 270 @@ -11170,17 +12439,23 @@ temperature = 250 [filament:Fillamentum Nylon FX256 @PG] inherits = Fillamentum Nylon FX256; *PAPG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fillamentum Nylon FX256 @PG 0.6] inherits = Fillamentum Nylon FX256 @PG; *PA06PG* filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fillamentum Nylon FX256 @PG 0.8] inherits = Fillamentum Nylon FX256 @PG; *PA08PG* filament_max_volumetric_speed = 11 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fillamentum Nylon FX256 @XL] +inherits = Fillamentum Nylon FX256 @PG; *PAXL* + +[filament:Fillamentum Nylon FX256 @XL 0.6] +inherits = Fillamentum Nylon FX256 @PG 0.6; *PA06XL* + +[filament:Fillamentum Nylon FX256 @XL 0.8] +inherits = Fillamentum Nylon FX256 @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA Pure Pro] inherits = *common* @@ -11217,21 +12492,27 @@ inherits = Fiberthree F3 PA Pure Pro; *PAPG* filament_max_volumetric_speed = 5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG 0.6] inherits = Fiberthree F3 PA Pure Pro @PG; *PA06PG* filament_max_volumetric_speed = 7 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG 0.8] inherits = Fiberthree F3 PA Pure Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA Pure Pro @XL] +inherits = Fiberthree F3 PA Pure Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA Pure Pro @XL 0.6] +inherits = Fiberthree F3 PA Pure Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA Pure Pro @XL 0.8] +inherits = Fiberthree F3 PA Pure Pro @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA-CF Pro] inherits = *common* @@ -11268,21 +12549,27 @@ inherits = Fiberthree F3 PA-CF Pro; *PAPG* filament_max_volumetric_speed = 5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG 0.6] inherits = Fiberthree F3 PA-CF Pro; *PA06PG* filament_max_volumetric_speed = 7 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG 0.8] inherits = Fiberthree F3 PA-CF Pro; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-CF Pro @XL] +inherits = Fiberthree F3 PA-CF Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA-CF Pro @XL 0.6] +inherits = Fiberthree F3 PA-CF Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA-CF Pro @XL 0.8] +inherits = Fiberthree F3 PA-CF Pro @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA-GF Pro] inherits = Fiberthree F3 PA-CF Pro @@ -11298,21 +12585,27 @@ inherits = Fiberthree F3 PA-GF Pro; *PAPG* filament_max_volumetric_speed = 5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro @PG 0.6] inherits = Fiberthree F3 PA-GF Pro @PG; *PA06PG* filament_max_volumetric_speed = 7 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro @PG 0.8] inherits = Fiberthree F3 PA-GF Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-GF Pro @XL] +inherits = Fiberthree F3 PA-GF Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA-GF Pro @XL 0.6] +inherits = Fiberthree F3 PA-GF Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA-GF Pro @XL 0.8] +inherits = Fiberthree F3 PA-GF Pro @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA-GF30 Pro] inherits = Prusament PC Blend Carbon Fiber @@ -11336,21 +12629,27 @@ inherits = Fiberthree F3 PA-GF30 Pro; *PAPG* filament_max_volumetric_speed = 6 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro @PG 0.6] inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA06PG* filament_max_volumetric_speed = 7.5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro @PG 0.8] inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-GF30 Pro @XL] +inherits = Fiberthree F3 PA-GF30 Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA-GF30 Pro @XL 0.6] +inherits = Fiberthree F3 PA-GF30 Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA-GF30 Pro @XL 0.8] +inherits = Fiberthree F3 PA-GF30 Pro @PG 0.8; *PA08XL* [filament:Taulman T-Glase] inherits = *PET* @@ -11368,15 +12667,21 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no [filament:Taulman T-Glase @PG] inherits = Taulman T-Glase; *PAPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase @PG 0.6] inherits = Taulman T-Glase @PG; *PA06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase @PG 0.8] inherits = Taulman T-Glase @PG; *PA08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Taulman T-Glase @XL] +inherits = Taulman T-Glase @PG; *PAXL* + +[filament:Taulman T-Glase @XL 0.6] +inherits = Taulman T-Glase @PG 0.6; *PA06XL* + +[filament:Taulman T-Glase @XL 0.8] +inherits = Taulman T-Glase @PG 0.8; *PA08XL* [filament:Verbatim PLA] inherits = *PLA* @@ -11396,6 +12701,15 @@ inherits = Verbatim PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Verbatim PLA @XL] +inherits = Verbatim PLA @PG; *PLAXL* + +[filament:Verbatim PLA @XL 0.6] +inherits = Verbatim PLA @PG 0.6; *PLA06XL* + +[filament:Verbatim PLA @XL 0.8] +inherits = Verbatim PLA @PG 0.8; *PLA08XL* + [filament:Verbatim BVOH] inherits = *common* filament_vendor = Verbatim @@ -11426,18 +12740,24 @@ first_layer_temperature = 215 temperature = 210 idle_temperature = 70 filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @PG 0.6] inherits = Verbatim BVOH @PG; *ABS06PG* filament_max_volumetric_speed = 5 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @PG 0.8] inherits = Verbatim BVOH @PG; *ABS08PG* temperature = 215 filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Verbatim BVOH @XL] +inherits = Verbatim BVOH @PG; *ABSXL*; *PVAXL* + +[filament:Verbatim BVOH @XL 0.6] +inherits = Verbatim BVOH @PG 0.6; *ABS06XL*; *PVAXL* + +[filament:Verbatim BVOH @XL 0.8] +inherits = Verbatim BVOH @PG 0.8; *ABS08XL*; *PVAXL* [filament:Verbatim BVOH @MMU] inherits = Verbatim BVOH @@ -11525,17 +12845,23 @@ temperature = 220 [filament:Verbatim PP @PG] inherits = Verbatim PP; *ABSPG* filament_max_volumetric_speed = 5 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PP @PG 0.6] inherits = Verbatim PP @PG; *ABS06PG* filament_max_volumetric_speed = 7 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PP @PG 0.8] inherits = Verbatim PP @PG; *ABS08PG* filament_max_volumetric_speed = 10 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Verbatim PP @XL] +inherits = Verbatim PP @PG; *ABSXL* + +[filament:Verbatim PP @XL 0.6] +inherits = Verbatim PP @PG 0.6; *ABS06XL* + +[filament:Verbatim PP @XL 0.8] +inherits = Verbatim PP @PG 0.8; *ABS08XL* [filament:FormFutura Centaur PP] inherits = *common* @@ -11577,6 +12903,15 @@ filament_max_volumetric_speed = 8 first_layer_temperature = 240 temperature = 240 +[filament:FormFutura Centaur PP @XL] +inherits = FormFutura Centaur PP @PG; *PETXL* + +[filament:FormFutura Centaur PP @XL 0.6] +inherits = FormFutura Centaur PP @PG 0.6; *PET06XL* + +[filament:FormFutura Centaur PP @XL 0.8] +inherits = FormFutura Centaur PP @PG 0.8; *PET08XL* + [filament:FormFutura Centaur PP @MINI] inherits = FormFutura Centaur PP filament_max_volumetric_speed = 3 @@ -17222,7 +18557,7 @@ printer_model = MINI printer_technology = FFF printer_variant = 0.4 printer_vendor = -thumbnails = 16x16,220x124 +thumbnails = 16x16,220x124,640x480 bed_shape = 0x0,180x0,180x180,0x180 default_filament_profile = "Prusament PLA" default_print_profile = 0.15mm QUALITY @MINI @@ -17344,8 +18679,7 @@ retract_length = 0.8 start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\nM862.1 P[nozzle_diameter]\n; set & wait for bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; set extruder temp for bed leveling\nM109 T0 R{((filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; wait for temp\n; home carriage, pick tool, home all\nG28 XY\nM84 E ; turn off E motor\nG28 Z\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F4800\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\n{if first_layer_bed_temperature[0] > 60}\nG0 Z70 F480 ; move away (a bit more) in Z\nG0 X30 Y{print_bed_min[1]} F6000 ; move away in X/Y for higher bed temperatures\n{endif}\nM106 S100 ; cool off the nozzle\nM107 ; stop cooling off the nozzle - turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nM104 S[first_layer_temperature] ; set extruder temp\nG1 Z10 F720 ; move away in Z\nG0 X30 Y-8 F6000 ; move next to the sheet\n; wait for extruder temp\nM109 T0 S{first_layer_temperature[0]}\n;\n; purge\n;\nG92 E0 ; reset extruder position\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -8 : -5.5)} ; move close to the sheet's edge\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E10 X40 Z0.2 F500 ; purge\nG0 X70 E9 F800 ; purge\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG92 E0 ; reset extruder position\n default_print_profile = 0.20mm QUALITY @XL 0.4 default_filament_profile = "Prusament PLA @PG" -thumbnails = 16x16,313x173,440x240 -thumbnails_format = PNG +thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 640x480/PNG gcode_flavor = marlin2 high_current_on_filament_swap = 0 retract_lift = 0.3 @@ -17369,7 +18703,7 @@ retract_restart_extra = 0,0,0,0,0 retract_restart_extra_toolchange = 0,0,0,0,0 wipe = 1,1,1,1,1 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D -start_gcode = M17 ; enable steppers\nM862.3 P "XL" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\n{if (is_extruder_used[0])}M862.1 T0 P{nozzle_diameter[0]}{endif}\n{if (is_extruder_used[1])}M862.1 T1 P{nozzle_diameter[1]}{endif}\n{if (is_extruder_used[2])}M862.1 T2 P{nozzle_diameter[2]}{endif}\n{if (is_extruder_used[3])}M862.1 T3 P{nozzle_diameter[3]}{endif}\n{if (is_extruder_used[4])}M862.1 T4 P{nozzle_diameter[4]}{endif}\n\n; turn off unused heaters\n{if ! is_extruder_used[0]} M104 T0 S0 {endif}\n{if ! is_extruder_used[1]} M104 T1 S0 {endif}\n{if ! is_extruder_used[2]} M104 T2 S0 {endif}\n{if ! is_extruder_used[3]} M104 T3 S0 {endif}\n{if ! is_extruder_used[4]} M104 T4 S0 {endif}\n\nM217 Z{max(zhop, 2.0)} ; set toolchange z hop to 2mm, or zhop variable from slicer if higher\n; set bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 T{initial_tool} S{((filament_type[initial_tool] == "PC" or filament_type[initial_tool] == "PA") ? (first_layer_temperature[initial_tool] - 25) : (filament_type[initial_tool] == "FLEX" ? 210 : 170))}\n; Home XY\nG28 XY\n; try picking tools used in print\n{if (is_extruder_used[0]) and (initial_tool != 0)}T0 S1 L0{endif}\n{if (is_extruder_used[1]) and (initial_tool != 1)}T1 S1 L0{endif}\n{if (is_extruder_used[2]) and (initial_tool != 2)}T2 S1 L0{endif}\n{if (is_extruder_used[3]) and (initial_tool != 3)}T3 S1 L0{endif}\n{if (is_extruder_used[4]) and (initial_tool != 4)}T4 S1 L0{endif}\n; select tool that will be used to home & MBL\nT{initial_tool} S1 L0\n; home Z with MBL tool\nM84 E ; turn off E motor\nG28 Z\nG0 Z10 ; add Z clearance\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F{(travel_speed * 60)}\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\nM107 ; turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W{(((is_extruder_used[4]) or ((is_extruder_used[3]) or (is_extruder_used[2]))) ? "300" : ((is_extruder_used[1]) ? "130" : "50"))} H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nG1 Z10 F720 ; move away in Z\nP0 S1 L1 D0; park the tool\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nP0 S1 L1 D0; park the tool\nM109 T0 S{first_layer_temperature[0]}\nT0 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X40 Z0.2 F500 ; purge while moving towards the sheet\nG0 X70 E9 F800 ; continue purging and wipe the nozzle\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[0]) ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nP0 S1 L1 D0; park the tool\nM109 T1 S{first_layer_temperature[1]}\nT1 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(1 == 0 ? 30 : (1 == 1 ? 150 : (1 == 2 ? 210 : 330)))} Y{(1 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X140 Z0.2 F500 ; purge while moving towards the sheet\nG0 X110 E9 F800 ; continue purging and wipe the nozzle\nG0 X{110 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{110 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[1]) ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nP0 S1 L1 D0; park the tool\nM109 T2 S{first_layer_temperature[2]}\nT2 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(2 == 0 ? 30 : (2 == 1 ? 150 : (2 == 2 ? 210 : 330)))} Y{(2 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X220 Z0.2 F500 ; purge while moving towards the sheet\nG0 X250 E9 F800 ; continue purging and wipe the nozzle\nG0 X{250 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{250 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[2]) ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nP0 S1 L1 D0; park the tool\nM109 T3 S{first_layer_temperature[3]}\nT3 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(3 == 0 ? 30 : (3 == 1 ? 150 : (3 == 2 ? 210 : 330)))} Y{(3 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X320 Z0.2 F500 ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[3]) ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nP0 S1 L1 D0; park the tool\nM109 T4 S{first_layer_temperature[4]}\nT4 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(4 == 0 ? 30 : (4 == 1 ? 150 : (4 == 2 ? 210 : 330)))} Y{(4 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X320 Z0.2 F500 ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[4]) ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nP0 S1 L1 D0; park the tool\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool} S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330)))} Y{(initial_tool < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F500 ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n +start_gcode = M17 ; enable steppers\nM862.3 P "XL" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\n{if (is_extruder_used[0])}M862.1 T0 P{nozzle_diameter[0]}{endif}\n{if (is_extruder_used[1])}M862.1 T1 P{nozzle_diameter[1]}{endif}\n{if (is_extruder_used[2])}M862.1 T2 P{nozzle_diameter[2]}{endif}\n{if (is_extruder_used[3])}M862.1 T3 P{nozzle_diameter[3]}{endif}\n{if (is_extruder_used[4])}M862.1 T4 P{nozzle_diameter[4]}{endif}\n\n; turn off unused heaters\n{if ! is_extruder_used[0]} M104 T0 S0 {endif}\n{if ! is_extruder_used[1]} M104 T1 S0 {endif}\n{if ! is_extruder_used[2]} M104 T2 S0 {endif}\n{if ! is_extruder_used[3]} M104 T3 S0 {endif}\n{if ! is_extruder_used[4]} M104 T4 S0 {endif}\n\nM217 Z{max(zhop, 2.0)} ; set toolchange z hop to 2mm, or zhop variable from slicer if higher\n; set bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nG0 Z5 ; add Z clearance\nM109 T{initial_tool} S{((filament_type[initial_tool] == "PC" or filament_type[initial_tool] == "PA") ? (first_layer_temperature[initial_tool] - 25) : (filament_type[initial_tool] == "FLEX" ? 210 : 170))}\n; Home XY\nG28 XY\n; try picking tools used in print\nG1 F{travel_speed * 60}\n{if (is_extruder_used[0]) and (initial_tool != 0)}T0 S1 L0 D0{endif}\n{if (is_extruder_used[1]) and (initial_tool != 1)}T1 S1 L0 D0{endif}\n{if (is_extruder_used[2]) and (initial_tool != 2)}T2 S1 L0 D0{endif}\n{if (is_extruder_used[3]) and (initial_tool != 3)}T3 S1 L0 D0{endif}\n{if (is_extruder_used[4]) and (initial_tool != 4)}T4 S1 L0 D0{endif}\n; select tool that will be used to home & MBL\nT{initial_tool} S1 L0 D0\n; home Z with MBL tool\nM84 E ; turn off E motor\nG28 Z\nG0 Z5 ; add Z clearance\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F{(travel_speed * 60)}\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z5 F480 ; move away in Z\nM107 ; turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W{(((is_extruder_used[4]) or ((is_extruder_used[3]) or (is_extruder_used[2]))) ? "300" : ((is_extruder_used[1]) ? "130" : "50"))} H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nG1 Z10 F720 ; move away in Z\nG1 F{travel_speed * 60}\nP0 S1 L1 D0; park the tool\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T0 S{first_layer_temperature[0]}\nT0 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[0])}10{else}30{endif} X40 Z0.2 F{if is_nil(filament_multitool_ramming[0])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X70 E9 F800 ; continue purging and wipe the nozzle\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[0]) ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T1 S{first_layer_temperature[1]}\nT1 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 30 : (1 == 1 ? 150 : (1 == 2 ? 210 : 330)))} Y{(1 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[1])}10{else}30{endif} X140 Z0.2 F{if is_nil(filament_multitool_ramming[1])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X110 E9 F800 ; continue purging and wipe the nozzle\nG0 X{110 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{110 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[1]) ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T2 S{first_layer_temperature[2]}\nT2 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 30 : (2 == 1 ? 150 : (2 == 2 ? 210 : 330)))} Y{(2 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[2])}10{else}30{endif} X220 Z0.2 F{if is_nil(filament_multitool_ramming[2])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X250 E9 F800 ; continue purging and wipe the nozzle\nG0 X{250 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{250 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[2]) ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T3 S{first_layer_temperature[3]}\nT3 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 30 : (3 == 1 ? 150 : (3 == 2 ? 210 : 330)))} Y{(3 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[3])}10{else}30{endif} X320 Z0.2 F{if is_nil(filament_multitool_ramming[3])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[3]) ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T4 S{first_layer_temperature[4]}\nT4 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 30 : (4 == 1 ? 150 : (4 == 2 ? 210 : 330)))} Y{(4 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[4])}10{else}30{endif} X320 Z0.2 F{if is_nil(filament_multitool_ramming[4])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[4]) ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool} S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330)))} Y{(initial_tool < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[initial_tool])}10{else}30{endif} X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if is_nil(filament_multitool_ramming[initial_tool])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position end_gcode = G4 ; wait\n\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+5, max_print_height)}{endif} ; Move print head up\nP0 S1 ; park tool\nM84 ; disable motors\n\n; turn off extruder heaters\n{if is_extruder_used[0]} M104 T0 S0 {endif}\n{if is_extruder_used[1]} M104 T1 S0 {endif}\n{if is_extruder_used[2]} M104 T2 S0 {endif}\n{if is_extruder_used[3]} M104 T3 S0 {endif}\n{if is_extruder_used[4]} M104 T4 S0 {endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow percentage\nM84 ; disable motors\nM77 ; stop print timer\n; max_layer_z = [max_layer_z] toolchange_gcode = ; Change Tool[previous_extruder] -> Tool[next_extruder] (layer [layer_num])\n{\nlocal max_speed_toolchange = 350.0;\nlocal wait_for_extruder_temp = true;\nposition[2] = position[2] + 2.0;\n\nlocal speed_toolchange = max_speed_toolchange;\nif travel_speed < max_speed_toolchange then\n speed_toolchange = travel_speed;\nendif\n"G1 F" + (speed_toolchange * 60) + "\n";\nif wait_for_extruder_temp and not((layer_num < 0) and (next_extruder == initial_tool)) then\n "P0 S1 L2 D0\n";\n "; " + layer_num + "\n";\n if layer_num == 0 then\n "M109 S" + first_layer_temperature[next_extruder] + " T" + next_extruder + "\n";\n else\n "M109 S" + temperature[next_extruder] + " T" + next_extruder + "\n";\n endif\nendif\n"T" + next_extruder + " S1 L0 D0\n";\n} color_change_gcode = M600 @@ -17644,8 +18978,7 @@ retract_length = 0.8 start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% default_print_profile = 0.20mm QUALITY @MK4 0.4 default_filament_profile = "Prusament PLA @PG" -thumbnails = 16x16,313x173,440x240 -thumbnails_format = PNG +thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 640x480/PNG gcode_flavor = marlin2 high_current_on_filament_swap = 0 retract_lift = 0.2 @@ -17738,9 +19071,9 @@ wipe = 0 retract_before_wipe = 80 retract_speed = 35 deretract_speed = 0 -start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "MK4" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0-RC+11963\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "MK4" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0+12056\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] -before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\nM74 W[extruded_weight_total] +before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n default_print_profile = 0.20mm SPEED @MK4IS 0.4 default_filament_profile = "Prusament PLA @MK4IS" @@ -17808,6 +19141,99 @@ min_layer_height = 0.2 default_print_profile = 0.40mm QUALITY @MK4IS 0.8 default_filament_profile = "Prusament PLA @PG 0.8" +; [printer:Original Prusa MK3.9 0.4 nozzle] +; inherits = *commonMK4* +; printer_model = MK3.9 +; printer_variant = 0.4 +; max_layer_height = 0.30 +; machine_limits_usage = emit_to_gcode +; machine_max_acceleration_e = 2500,5000 +; machine_max_acceleration_extruding = 4000,2000 +; machine_max_acceleration_retracting = 1200,2000 +; machine_max_acceleration_travel = 4000,1250 +; machine_max_acceleration_x = 4000,2000 +; machine_max_acceleration_y = 4000,2000 +; machine_max_acceleration_z = 200,2000 +; machine_max_feedrate_e = 100,120 +; machine_max_feedrate_x = 300,100 +; machine_max_feedrate_y = 300,100 +; machine_max_feedrate_z = 40,12 +; machine_max_jerk_e = 10,1.5 +; machine_max_jerk_x = 8,8 +; machine_max_jerk_y = 8,8 +; machine_max_jerk_z = 2,0.4 +; machine_min_extruding_rate = 0,0 +; machine_min_travel_rate = 0,0 +; max_print_height = 220 +; printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_MODEL_MK4IS\nPRINTER_MODEL_MK3.9\nPG +; retract_length = 0.7 +; wipe = 0 +; retract_before_wipe = 80 +; retract_speed = 35 +; deretract_speed = 0 +; start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "[printer_model]" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0+12056\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +; end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] +; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n +; default_print_profile = 0.20mm SPEED @MK4IS 0.4 +; default_filament_profile = "Prusament PLA @PGIS" + +; [printer:Original Prusa MK3.9 0.25 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.25 +; nozzle_diameter = 0.25 +; retract_length = 0.8 +; retract_lift = 0.15 +; max_layer_height = 0.15 +; min_layer_height = 0.05 +; default_print_profile = 0.12mm STRUCTURAL @MK4IS 0.25 + +; [printer:Original Prusa MK3.9 0.3 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.3 +; nozzle_diameter = 0.3 +; retract_length = 0.7 +; max_layer_height = 0.22 +; min_layer_height = 0.05 +; default_print_profile = 0.16mm STRUCTURAL @MK4IS 0.3 + +; [printer:Original Prusa MK3.9 0.5 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.5 +; nozzle_diameter = 0.5 +; retract_length = 0.7 +; max_layer_height = 0.32 +; min_layer_height = 0.07 +; deretract_speed = 25 +; wipe = 1 +; default_print_profile = 0.20mm SPEED @MK4IS 0.5 + +; [printer:Original Prusa MK3.9 0.6 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.6 +; nozzle_diameter = 0.6 +; retract_length = 0.7 +; retract_lift = 0.2 +; max_layer_height = 0.40 +; min_layer_height = 0.15 +; deretract_speed = 25 +; wipe = 1 +; default_print_profile = 0.25mm SPEED @MK4IS 0.6 + +; [printer:Original Prusa MK3.9 0.8 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.8 +; nozzle_diameter = 0.8 +; retract_length = 0.6 +; wipe = 1 +; retract_before_wipe = 50% +; retract_lift = 0.25 +; retract_speed = 25 +; deretract_speed = 15 +; max_layer_height = 0.6 +; min_layer_height = 0.2 +; default_print_profile = 0.40mm QUALITY @MK4IS 0.8 +; default_filament_profile = "Prusament PLA @PG 0.8" + [printer:Original Prusa SL1] printer_technology = SLA printer_model = SL1 diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 0d627aa837..f3441a8d62 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -2,8 +2,12 @@ // Why? #define _WIN32_WINNT 0x0502 // The standard Windows includes. - #define WIN32_LEAN_AND_MEAN - #define NOMINMAX + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif // WIN32_LEAN_AND_MEAN + #ifndef NOMINMAX + #define NOMINMAX + #endif // NOMINMAX #include #include #ifdef SLIC3R_GUI @@ -763,6 +767,7 @@ bool CLI::setup(int argc, char **argv) set_var_dir((path_resources / "icons").string()); set_local_dir((path_resources / "localization").string()); set_sys_shapes_dir((path_resources / "shapes").string()); + set_custom_gcodes_dir((path_resources / "custom_gcodes").string()); // Parse all command line options into a DynamicConfig. // If any option is unsupported, print usage and abort immediately. diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp index 90bd2d89f2..06f15aa5ad 100644 --- a/src/PrusaSlicer_app_msvc.cpp +++ b/src/PrusaSlicer_app_msvc.cpp @@ -1,14 +1,16 @@ // Why? #define _WIN32_WINNT 0x0502 // The standard Windows includes. -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX + #define NOMINMAX +#endif // NOMINMAX #include #include #include - - #ifdef SLIC3R_GUI extern "C" { diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index cd51ccc103..6cbf45b79c 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -84,15 +84,24 @@ inline IntPoint IntPoint2d(cInt x, cInt y) ); } -inline cInt Round(double val) +// Fast rounding upwards. +inline double FRound(double a) { - double v = val < 0 ? val - 0.5 : val + 0.5; + // Why does Java Math.round(0.49999999999999994) return 1? + // https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1 + return a == 0.49999999999999994 ? 0 : floor(a + 0.5); +} + +template +inline IType Round(double val) +{ + double v = FRound(val); #if defined(CLIPPERLIB_INT32) && ! defined(NDEBUG) static constexpr const double hi = 65536 * 16383; if (v > hi || -v > hi) throw clipperException("Coordinate outside allowed range"); #endif - return static_cast(v); + return static_cast(v); } // Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional. @@ -340,7 +349,7 @@ inline cInt TopX(TEdge &edge, const cInt currentY) { return (currentY == edge.Top.y()) ? edge.Top.x() : - edge.Bot.x() + Round(edge.Dx *(currentY - edge.Bot.y())); + edge.Bot.x() + Round(edge.Dx *(currentY - edge.Bot.y())); } //------------------------------------------------------------------------------ @@ -350,65 +359,53 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.z() = 0; #endif - double b1, b2; if (Edge1.Dx == Edge2.Dx) { ip.y() = Edge1.Curr.y(); ip.x() = TopX(Edge1, ip.y()); return; } - else if (Edge1.Delta.x() == 0) + + int64_t y; + if (Edge1.Delta.x() == 0) { ip.x() = Edge1.Bot.x(); - if (IsHorizontal(Edge2)) - ip.y() = Edge2.Bot.y(); - else - { - b2 = Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx); - ip.y() = Round(ip.x() / Edge2.Dx + b2); - } + y = IsHorizontal(Edge2) ? + Edge2.Bot.y() : + Round(ip.x() / Edge2.Dx + Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx)); + } else if (Edge2.Delta.x() == 0) { ip.x() = Edge2.Bot.x(); - if (IsHorizontal(Edge1)) - ip.y() = Edge1.Bot.y(); - else - { - b1 = Edge1.Bot.y() - (Edge1.Bot.x() / Edge1.Dx); - ip.y() = Round(ip.x() / Edge1.Dx + b1); - } - } - else + y = IsHorizontal(Edge1) ? + Edge1.Bot.y() : + Round(ip.x() / Edge1.Dx + Edge1.Bot.y() - (Edge1.Bot.x() / Edge1.Dx)); + } + else { - b1 = double(Edge1.Bot.x()) - double(Edge1.Bot.y()) * Edge1.Dx; - b2 = double(Edge2.Bot.x()) - double(Edge2.Bot.y()) * Edge2.Dx; + double b1 = double(Edge1.Bot.x()) - double(Edge1.Bot.y()) * Edge1.Dx; + double b2 = double(Edge2.Bot.x()) - double(Edge2.Bot.y()) * Edge2.Dx; double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.y() = Round(q); - ip.x() = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? - Round(Edge1.Dx * q + b1) : - Round(Edge2.Dx * q + b2); + y = Round(q); + ip.x() = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? + Round(Edge1.Dx * q + b1) : + Round(Edge2.Dx * q + b2); } - if (ip.y() < Edge1.Top.y() || ip.y() < Edge2.Top.y()) + ip.y() = cInt(y); + if (y < Edge1.Top.y() || y < Edge2.Top.y()) { - if (Edge1.Top.y() > Edge2.Top.y()) - ip.y() = Edge1.Top.y(); - else - ip.y() = Edge2.Top.y(); - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.x() = TopX(Edge1, ip.y()); - else - ip.x() = TopX(Edge2, ip.y()); - } + ip.y() = (Edge1.Top.y() > Edge2.Top.y() ? Edge1 : Edge2).Top.y(); + y = ip.y(); + ip.x() = TopX(std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx) ? Edge1 : Edge2, ip.y()); + } //finally, don't allow 'ip' to be BELOW curr.y() (ie bottom of scanbeam) ... - if (ip.y() > Edge1.Curr.y()) + if (y > Edge1.Curr.y()) { ip.y() = Edge1.Curr.y(); //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.x() = TopX(Edge2, ip.y()); else - ip.x() = TopX(Edge1, ip.y()); + ip.x() = TopX(std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx) ? Edge2 : Edge1, ip.y()); } } //------------------------------------------------------------------------------ @@ -3539,8 +3536,8 @@ void ClipperOffset::DoOffset(double delta) for (cInt j = 1; j <= steps; j++) { m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[0].x() + X * delta), - Round(m_srcPoly[0].y() + Y * delta))); + Round(m_srcPoly[0].x() + X * delta), + Round(m_srcPoly[0].y() + Y * delta))); double X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; @@ -3552,8 +3549,8 @@ void ClipperOffset::DoOffset(double delta) for (int j = 0; j < 4; ++j) { m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[0].x() + X * delta), - Round(m_srcPoly[0].y() + Y * delta))); + Round(m_srcPoly[0].x() + X * delta), + Round(m_srcPoly[0].y() + Y * delta))); if (X < 0) X = 1; else if (Y < 0) Y = 1; else X = -1; @@ -3606,9 +3603,9 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { int j = len - 1; - pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); m_destPoly.emplace_back(pt1); - pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); m_destPoly.emplace_back(pt1); } else @@ -3633,9 +3630,9 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { - pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); m_destPoly.emplace_back(pt1); - pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); m_destPoly.emplace_back(pt1); } else @@ -3663,8 +3660,8 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() ); if (cosA > 0) // angle => 0 degrees { - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); return; } //else angle => 180 degrees @@ -3674,11 +3671,11 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) if (m_sinA * m_delta < 0) { - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); m_destPoly.emplace_back(m_srcPoly[j]); - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } else switch (jointype) @@ -3702,19 +3699,19 @@ void ClipperOffset::DoSquare(int j, int k) double dx = std::tan(std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4); m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), - Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); + Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), + Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), - Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); + Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), + Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); } //------------------------------------------------------------------------------ void ClipperOffset::DoMiter(int j, int k, double r) { double q = m_delta / r; - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), - Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), + Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); } //------------------------------------------------------------------------------ @@ -3722,21 +3719,21 @@ void ClipperOffset::DoRound(int j, int k) { double a = std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()); - auto steps = std::max(Round(m_StepsPerRad * std::fabs(a)), 1); + auto steps = std::max(Round(m_StepsPerRad * std::fabs(a)), 1); double X = m_normals[k].x(), Y = m_normals[k].y(), X2; for (int i = 0; i < steps; ++i) { m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + X * m_delta), - Round(m_srcPoly[j].y() + Y * m_delta))); + Round(m_srcPoly[j].x() + X * m_delta), + Round(m_srcPoly[j].y() + Y * m_delta))); X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; } m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); + Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } //------------------------------------------------------------------------------ diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index f12e590d6f..3fdcda0c5c 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -349,7 +349,7 @@ public: return dist; } - std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) + std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) const { return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); } diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index b79d05d534..5e9575b015 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -73,7 +73,9 @@ private: template> static void construct(BoundingBoxType &out, It from, It to) { - if (from != to) { + if (from == to) { + out.defined = false; + } else { auto it = from; out.min = it->template cast(); out.max = out.min; diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index de5f2dc9d5..b6cdf4dde7 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -495,8 +495,10 @@ static void make_inner_brim(const Print &print, loops = union_pt_chained_outside_in(loops); std::reverse(loops.begin(), loops.end()); - extrusion_entities_append_loops(brim.entities, std::move(loops), ExtrusionRole::Skirt, float(flow.mm3_per_mm()), - float(flow.width()), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops(brim.entities, std::move(loops), + ExtrusionAttributes{ + ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } }); } // Produce brim lines around those objects, that have the brim enabled. @@ -677,7 +679,9 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) { auto *loop = new ExtrusionLoop(); brim.entities.emplace_back(loop); - loop->paths.emplace_back(ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); + loop->paths.emplace_back(ExtrusionAttributes{ + ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } }); Points &points = loop->paths.front().polyline.points; points.reserve(first_path.size()); for (const ClipperLib_Z::IntPoint &pt : first_path) @@ -688,7 +692,9 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance ExtrusionEntityCollection this_loop_trimmed; this_loop_trimmed.entities.reserve(j - i); for (; i < j; ++ i) { - this_loop_trimmed.entities.emplace_back(new ExtrusionPath(ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()))); + this_loop_trimmed.entities.emplace_back(new ExtrusionPath({ + ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } })); const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; Points &points = dynamic_cast(this_loop_trimmed.entities.back())->polyline.points; points.reserve(path.size()); @@ -704,7 +710,9 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance } } } else { - extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), + ExtrusionAttributes{ ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } }); } make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim); diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 1aa2eec86b..c4f2a5e64c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -21,6 +21,11 @@ if (TARGET OpenVDB::openvdb) set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp OpenVDBUtilsLegacy.hpp) endif() +find_package(LibBGCode REQUIRED COMPONENTS Convert) +slic3r_remap_configs(LibBGCode::bgcode_core RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_binarize RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_convert RelWithDebInfo Release) + set(SLIC3R_SOURCES pchheader.cpp pchheader.hpp @@ -151,8 +156,12 @@ set(SLIC3R_SOURCES GCode/ConflictChecker.hpp GCode/CoolingBuffer.cpp GCode/CoolingBuffer.hpp + GCode/ExtrusionProcessor.cpp + GCode/ExtrusionProcessor.hpp GCode/FindReplace.cpp GCode/FindReplace.hpp + GCode/GCodeWriter.cpp + GCode/GCodeWriter.hpp GCode/PostProcessor.cpp GCode/PostProcessor.hpp GCode/PressureEqualizer.cpp @@ -165,10 +174,16 @@ set(SLIC3R_SOURCES GCode/SpiralVase.hpp GCode/SeamPlacer.cpp GCode/SeamPlacer.hpp + GCode/SmoothPath.cpp + GCode/SmoothPath.hpp GCode/ToolOrdering.cpp GCode/ToolOrdering.hpp + GCode/Wipe.cpp + GCode/Wipe.hpp GCode/WipeTower.cpp GCode/WipeTower.hpp + GCode/WipeTowerIntegration.cpp + GCode/WipeTowerIntegration.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp GCode/AvoidCrossingPerimeters.cpp @@ -179,10 +194,10 @@ set(SLIC3R_SOURCES GCodeReader.hpp # GCodeSender.cpp # GCodeSender.hpp - GCodeWriter.cpp - GCodeWriter.hpp Geometry.cpp Geometry.hpp + Geometry/ArcWelder.cpp + Geometry/ArcWelder.hpp Geometry/Bicubic.hpp Geometry/Circle.cpp Geometry/Circle.hpp @@ -570,6 +585,7 @@ target_link_libraries(libslic3r ZLIB::ZLIB JPEG::JPEG qoi + LibBGCode::bgcode_convert ) if (APPLE) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index ff911c2853..63e2b3821e 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -37,12 +37,15 @@ #include #include #include +#include #include #include #include #include #include +#include + //FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion) // This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy(). #include "PrintConfig.hpp" @@ -331,12 +334,9 @@ void ConfigDef::finalize() if (def.type == coEnum) { assert(def.enum_def); assert(def.enum_def->is_valid_closed_enum()); - assert(def.gui_type != ConfigOptionDef::GUIType::i_enum_open && - def.gui_type != ConfigOptionDef::GUIType::f_enum_open && - def.gui_type != ConfigOptionDef::GUIType::select_open); + assert(! def.is_gui_type_enum_open()); def.enum_def->finalize_closed_enum(); - } else if (def.gui_type == ConfigOptionDef::GUIType::i_enum_open || def.gui_type == ConfigOptionDef::GUIType::f_enum_open || - def.gui_type == ConfigOptionDef::GUIType::select_open) { + } else if (def.is_gui_type_enum_open()) { assert(def.enum_def); assert(def.enum_def->is_valid_open_enum()); assert(def.gui_type != ConfigOptionDef::GUIType::i_enum_open || def.type == coInt || def.type == coInts); @@ -740,11 +740,37 @@ void ConfigBase::setenv_() const } } -ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { - return is_gcode_file(file) ? - this->load_from_gcode_file(file, compatibility_rule) : - this->load_from_ini(file, compatibility_rule); + enum class EFileType + { + Ini, + AsciiGCode, + BinaryGCode + }; + + EFileType file_type; + + if (is_gcode_file(filename)) { + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); + + std::vector cs_buffer(65536); + using namespace bgcode::core; + file_type = (is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + fclose(file); + } + else + file_type = EFileType::Ini; + + switch (file_type) + { + case EFileType::Ini: { return this->load_from_ini(filename, compatibility_rule); } + case EFileType::AsciiGCode: { return this->load_from_gcode_file(filename, compatibility_rule);} + case EFileType::BinaryGCode: { return this->load_from_binary_gcode_file(filename, compatibility_rule);} + default: { throw Slic3r::RuntimeError(format("Invalid file %1%", filename)); } + } } ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -948,15 +974,15 @@ private: }; // Load the config keys from the tail of a G-code file. -ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { // Read a 64k block from the end of the G-code. - boost::nowide::ifstream ifs(file, std::ifstream::binary); + boost::nowide::ifstream ifs(filename, std::ifstream::binary); // Look for Slic3r or PrusaSlicer header. // Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user. bool has_delimiters = false; { - static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; + static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; static constexpr const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; std::string header; bool header_found = false; @@ -1006,7 +1032,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo break; } if (! end_found) - throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", file)); + throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", filename)); std::string key, value; while (reader.getline(line)) { if (line == "; prusaslicer_config = begin") { @@ -1029,7 +1055,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo } } if (! begin_found) - throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", file)); + throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", filename)); } else { @@ -1037,8 +1063,8 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo // Try a heuristics reading the G-code from back. ifs.seekg(0, ifs.end); auto file_length = ifs.tellg(); - auto data_length = std::min(65535, file_length - header_end_pos); - ifs.seekg(file_length - data_length, ifs.beg); + auto data_length = std::min(65535, file_length - header_end_pos); + ifs.seekg(file_length - data_length, ifs.beg); std::vector data(size_t(data_length) + 1, 0); ifs.read(data.data(), data_length); ifs.close(); @@ -1046,7 +1072,48 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo } if (key_value_pairs < 80) - throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); + throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs)); + return std::move(substitutions_ctxt.substitutions); +} + +ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); + + FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (file.f == nullptr) + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); + + using namespace bgcode::core; + using namespace bgcode::binarize; + std::vector cs_buffer(65536); + EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, + std::string(translate_result(res)))); + + FileHeader file_header; + res = read_header(*file.f, file_header, nullptr); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file %1%: %2%", filename, std::string(translate_result(res)))); + + // searches for config block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file %1%: %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) + throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file %1%", filename)); + SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file %1%: %2%", filename, std::string(translate_result(res)))); + + // extracts data from block + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + this->set_deserialize(key, value, substitutions_ctxt); + } + return std::move(substitutions_ctxt.substitutions); } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 5b4e06b37c..09d283a327 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1836,6 +1836,8 @@ public: // Close parameter, string value could be one of the list values. select_close, }; + static bool is_gui_type_enum_open(const GUIType gui_type) + { return gui_type == ConfigOptionDef::GUIType::i_enum_open || gui_type == ConfigOptionDef::GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open; } // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map. t_config_option_key opt_key; @@ -1853,6 +1855,8 @@ public: // Create a default option to be inserted into a DynamicConfig. ConfigOption* create_default_option() const; + bool is_scalar() const { return (int(this->type) & int(coVectorType)) == 0; } + template ConfigOption* load_option_from_archive(Archive &archive) const { if (this->nullable) { switch (this->type) { @@ -1923,6 +1927,7 @@ public: // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, // "select_open" - to open a selection dialog (currently only a serial port selection). GUIType gui_type { GUIType::undefined }; + bool is_gui_type_enum_open() const { return is_gui_type_enum_open(this->gui_type); } // Usually empty. Otherwise "serialized" or "show_value" // The flags may be combined. // "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon. @@ -1986,7 +1991,7 @@ public: void set_enum_values(GUIType gui_type, const std::initializer_list il) { this->enum_def_new(); - assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == GUIType::select_open); + assert(is_gui_type_enum_open(gui_type)); this->gui_type = gui_type; enum_def->set_values(il); } @@ -2098,6 +2103,7 @@ public: out.push_back(kvp.first); return out; } + bool empty() const { return options.empty(); } // Iterate through all of the CLI options and write them to a stream. std::ostream& print_cli_help( @@ -2328,7 +2334,8 @@ public: // Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF. // Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment). ConfigSubstitutions load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule); - ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); + ConfigSubstitutions load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule); + ConfigSubstitutions load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule); ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule); void save(const std::string &file) const; diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index a3ec6cc069..7b950b8d7c 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -5,7 +5,7 @@ #include "CustomGCode.hpp" #include "Config.hpp" #include "GCode.hpp" -#include "GCodeWriter.hpp" +#include "GCode/GCodeWriter.hpp" namespace Slic3r { diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index 7d08d98958..18b385b401 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -8,7 +8,7 @@ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #include "Extruder.hpp" -#include "GCodeWriter.hpp" +#include "GCode/GCodeWriter.hpp" #include "PrintConfig.hpp" namespace Slic3r { @@ -25,6 +25,7 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config) : std::pair Extruder::extrude(double dE) { + assert(! std::isnan(dE)); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -46,7 +47,8 @@ std::pair Extruder::extrude(double dE) value supplied will overwrite the previous one if any. */ std::pair Extruder::retract(double retract_length, double restart_extra) { - assert(restart_extra >= 0); + assert(! std::isnan(retract_length)); + assert(! std::isnan(restart_extra) && restart_extra >= 0); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index fa00a9a456..b5a9868e9b 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -9,6 +9,7 @@ #include "ExtrusionEntityCollection.hpp" #include "ExPolygon.hpp" #include "ClipperUtils.hpp" +#include "Exception.hpp" #include "Extruder.hpp" #include "Flow.hpp" #include @@ -16,7 +17,7 @@ #include namespace Slic3r { - + void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const { this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval); @@ -45,12 +46,12 @@ double ExtrusionPath::length() const void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const { for (const Polyline &polyline : polylines) - collection->entities.emplace_back(new ExtrusionPath(polyline, *this)); + collection->entities.emplace_back(new ExtrusionPath(polyline, this->attributes())); } void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon)); + polygons_append(out, offset(this->polyline, float(scale_(m_attributes.width/2)) + scaled_epsilon)); } void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const @@ -58,8 +59,8 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale // Instantiating the Flow class to get the line spacing. // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. bool bridge = this->role().is_bridge(); - assert(! bridge || this->width == this->height); - auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f); + assert(! bridge || m_attributes.width == m_attributes.height); + auto flow = bridge ? Flow::bridging_flow(m_attributes.width, 0.f) : Flow(m_attributes.width, m_attributes.height, 0.f); polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } @@ -94,7 +95,7 @@ double ExtrusionMultiPath::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits::max(); for (const ExtrusionPath &path : this->paths) - min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm); + min_mm3_per_mm = std::min(min_mm3_per_mm, path.min_mm3_per_mm()); return min_mm3_per_mm; } @@ -119,21 +120,34 @@ Polyline ExtrusionMultiPath::as_polyline() const return out; } -bool ExtrusionLoop::make_clockwise() +double ExtrusionLoop::area() const { - bool was_ccw = this->polygon().is_counter_clockwise(); - if (was_ccw) this->reverse(); - return was_ccw; -} - -bool ExtrusionLoop::make_counter_clockwise() -{ - bool was_cw = this->polygon().is_clockwise(); - if (was_cw) this->reverse(); - return was_cw; + double a = 0; + for (const ExtrusionPath &path : this->paths) { + assert(path.size() >= 2); + if (path.size() >= 2) { + // Assumming that the last point of one path segment is repeated at the start of the following path segment. + auto it = path.polyline.points.begin(); + Point prev = *it ++; + for (; it != path.polyline.points.end(); ++ it) { + a += cross2(prev.cast(), it->cast()); + prev = *it; + } + } + } + return a * 0.5; } void ExtrusionLoop::reverse() +{ +#if 0 + this->reverse_loop(); +#else + throw Slic3r::LogicError("ExtrusionLoop::reverse() must NOT be called"); +#endif +} + +void ExtrusionLoop::reverse_loop() { for (ExtrusionPath &path : this->paths) path.reverse(); @@ -255,8 +269,8 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const // now split path_idx in two parts const ExtrusionPath &path = this->paths[path_idx]; - ExtrusionPath p1(path.role(), path.mm3_per_mm, path.width, path.height); - ExtrusionPath p2(path.role(), path.mm3_per_mm, path.width, path.height); + ExtrusionPath p1(path.attributes()); + ExtrusionPath p2(path.attributes()); path.polyline.split_at(p, &p1.polyline, &p2.polyline); if (this->paths.size() == 1) { @@ -323,7 +337,7 @@ double ExtrusionLoop::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits::max(); for (const ExtrusionPath &path : this->paths) - min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm); + min_mm3_per_mm = std::min(min_mm3_per_mm, path.min_mm3_per_mm()); return min_mm3_per_mm; } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 55d476ea03..6238447c2c 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -10,10 +10,12 @@ #include "libslic3r.h" #include "ExtrusionRole.hpp" +#include "Flow.hpp" #include "Polygon.hpp" #include "Polyline.hpp" #include +#include #include #include @@ -62,28 +64,91 @@ public: virtual double total_volume() const = 0; }; -typedef std::vector ExtrusionEntitiesPtr; +using ExtrusionEntitiesPtr = std::vector; + +// Const reference for ordering extrusion entities without having to modify them. +class ExtrusionEntityReference final +{ +public: + ExtrusionEntityReference() = delete; + ExtrusionEntityReference(const ExtrusionEntity &extrusion_entity, bool flipped) : + m_extrusion_entity(&extrusion_entity), m_flipped(flipped) {} + ExtrusionEntityReference operator=(const ExtrusionEntityReference &rhs) + { m_extrusion_entity = rhs.m_extrusion_entity; m_flipped = rhs.m_flipped; return *this; } + + const ExtrusionEntity& extrusion_entity() const { return *m_extrusion_entity; } + template + const Type* cast() const { return dynamic_cast(m_extrusion_entity); } + bool flipped() const { return m_flipped; } + +private: + const ExtrusionEntity *m_extrusion_entity; + bool m_flipped; +}; + +using ExtrusionEntityReferences = std::vector; + +struct ExtrusionFlow +{ + ExtrusionFlow() = default; + ExtrusionFlow(double mm3_per_mm, float width, float height) : + mm3_per_mm{ mm3_per_mm }, width{ width }, height{ height } {} + ExtrusionFlow(const Flow &flow) : + mm3_per_mm(flow.mm3_per_mm()), width(flow.width()), height(flow.height()) {} + + // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. + double mm3_per_mm{ -1. }; + // Width of the extrusion, used for visualization purposes. + float width{ -1.f }; + // Height of the extrusion, used for visualization purposes. + float height{ -1.f }; +}; + +inline bool operator==(const ExtrusionFlow &lhs, const ExtrusionFlow &rhs) +{ + return lhs.mm3_per_mm == rhs.mm3_per_mm && lhs.width == rhs.width && lhs.height == rhs.height; +} + +struct OverhangAttributes { + float start_distance_from_prev_layer; + float end_distance_from_prev_layer; + float proximity_to_curled_lines; //value between 0 and 1 +}; + +struct ExtrusionAttributes : ExtrusionFlow +{ + ExtrusionAttributes() = default; + ExtrusionAttributes(ExtrusionRole role) : role{ role } {} + ExtrusionAttributes(ExtrusionRole role, const Flow &flow) : role{ role }, ExtrusionFlow{ flow } {} + ExtrusionAttributes(ExtrusionRole role, const ExtrusionFlow &flow) : role{ role }, ExtrusionFlow{ flow } {} + + // What is the role / purpose of this extrusion? + ExtrusionRole role{ ExtrusionRole::None }; + // OVerhangAttributes are currently computed for perimeters if dynamic overhangs are enabled. + // They are used to control fan and print speed in export. + std::optional overhang_attributes; +}; + +inline bool operator==(const ExtrusionAttributes &lhs, const ExtrusionAttributes &rhs) +{ + return static_cast(lhs) == static_cast(rhs) && + lhs.role == rhs.role; +} class ExtrusionPath : public ExtrusionEntity { public: Polyline polyline; - // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. - double mm3_per_mm; - // Width of the extrusion, used for visualization purposes. - float width; - // Height of the extrusion, used for visualization purposes. - float height; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {} - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {} - ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionRole role) : m_attributes{ role } {} + ExtrusionPath(const ExtrusionAttributes &attributes) : m_attributes(attributes) {} + ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), m_attributes(rhs.m_attributes) {} + ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), m_attributes(rhs.m_attributes) {} + ExtrusionPath(const Polyline &polyline, const ExtrusionAttributes &attribs) : polyline(polyline), m_attributes(attribs) {} + ExtrusionPath(Polyline &&polyline, const ExtrusionAttributes &attribs) : polyline(std::move(polyline)), m_attributes(attribs) {} - ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->polyline = rhs.polyline; m_attributes = rhs.m_attributes; return *this; } + ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->polyline = std::move(rhs.polyline); m_attributes = rhs.m_attributes; return *this; } ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); } // Create a new object, initialize it with this object using the move semantics. @@ -104,35 +169,46 @@ public: void clip_end(double distance); void simplify(double tolerance); double length() const override; - ExtrusionRole role() const override { return m_role; } + + const ExtrusionAttributes& attributes() const { return m_attributes; } + ExtrusionRole role() const override { return m_attributes.role; } + float width() const { return m_attributes.width; } + float height() const { return m_attributes.height; } + double mm3_per_mm() const { return m_attributes.mm3_per_mm; } + // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. + double min_mm3_per_mm() const override { return m_attributes.mm3_per_mm; } + std::optional& overhang_attributes_mutable() { return m_attributes.overhang_attributes; } + // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. - void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. - void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; - Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; + Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } - Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const + Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } - // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. - double min_mm3_per_mm() const override { return this->mm3_per_mm; } - Polyline as_polyline() const override { return this->polyline; } - void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } - void collect_points(Points &dst) const override { append(dst, this->polyline.points); } - double total_volume() const override { return mm3_per_mm * unscale(length()); } + + Polyline as_polyline() const override { return this->polyline; } + void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } + void collect_points(Points &dst) const override { append(dst, this->polyline.points); } + double total_volume() const override { return m_attributes.mm3_per_mm * unscale(length()); } private: - void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; + void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; - ExtrusionRole m_role; + ExtrusionAttributes m_attributes; }; class ExtrusionPathOriented : public ExtrusionPath { public: - ExtrusionPathOriented(ExtrusionRole role, double mm3_per_mm, float width, float height) : ExtrusionPath(role, mm3_per_mm, width, height) {} + ExtrusionPathOriented(const ExtrusionAttributes &attribs) : ExtrusionPath(attribs) {} + ExtrusionPathOriented(const Polyline &polyline, const ExtrusionAttributes &attribs) : ExtrusionPath(polyline, attribs) {} + ExtrusionPathOriented(Polyline &&polyline, const ExtrusionAttributes &attribs) : ExtrusionPath(std::move(polyline), attribs) {} + ExtrusionEntity* clone() const override { return new ExtrusionPathOriented(*this); } // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionPathOriented(std::move(*this)); } @@ -199,7 +275,8 @@ class ExtrusionLoop : public ExtrusionEntity public: ExtrusionPaths paths; - ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {} + ExtrusionLoop() = default; + ExtrusionLoop(ExtrusionLoopRole role) : m_loop_role(role) {} ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {} ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {} ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) @@ -211,16 +288,21 @@ public: ExtrusionEntity* clone() const override{ return new ExtrusionLoop (*this); } // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionLoop(std::move(*this)); } - bool make_clockwise(); - bool make_counter_clockwise(); - void reverse() override; - const Point& first_point() const override { return this->paths.front().polyline.points.front(); } - const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } - const Point& middle_point() const override { auto& path = this->paths[this->paths.size() / 2]; return path.polyline.points[path.polyline.size() / 2]; } - Polygon polygon() const; - double length() const override; - bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled(0.001)); - void split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon = scaled(0.001)); + double area() const; + bool is_counter_clockwise() const { return this->area() > 0; } + bool is_clockwise() const { return this->area() < 0; } + // Reverse shall never be called on ExtrusionLoop using a virtual function call, it is most likely never what one wants, + // as this->can_reverse() returns false for an ExtrusionLoop. + void reverse() override; + // Used by PerimeterGenerator to reorient extrusion loops. + void reverse_loop(); + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } + const Point& middle_point() const override { auto& path = this->paths[this->paths.size() / 2]; return path.polyline.points[path.polyline.size() / 2]; } + Polygon polygon() const; + double length() const override; + bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled(0.001)); + void split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon = scaled(0.001)); struct ClosestPathPoint { size_t path_idx; size_t segment_idx; @@ -266,59 +348,51 @@ public: #endif /* NDEBUG */ private: - ExtrusionLoopRole m_loop_role; + ExtrusionLoopRole m_loop_role{ elrDefault }; }; -inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); - dst.back().polyline = polyline; - } + if (polyline.is_valid()) + dst.emplace_back(polyline, attributes); } -inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); - dst.back().polyline = std::move(polyline); - } + if (polyline.is_valid()) + dst.emplace_back(std::move(polyline), attributes); polylines.clear(); } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, const ExtrusionAttributes &attributes, bool can_reverse = true) { dst.reserve(dst.size() + polylines.size()); for (const Polyline &polyline : polylines) - if (polyline.is_valid()) { - ExtrusionPath* extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); - dst.push_back(extrusion_path); - extrusion_path->polyline = polyline; - } + if (polyline.is_valid()) + dst.emplace_back(can_reverse ? new ExtrusionPath(polyline, attributes) : new ExtrusionPathOriented(polyline, attributes)); } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, const ExtrusionAttributes &attributes, bool can_reverse = true) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); - dst.push_back(extrusion_path); - extrusion_path->polyline = std::move(polyline); - } + if (polyline.is_valid()) + dst.emplace_back(can_reverse ? + new ExtrusionPath(std::move(polyline), attributes) : + new ExtrusionPathOriented(std::move(polyline), attributes)); polylines.clear(); } -inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + loops.size()); for (Polygon &poly : loops) { if (poly.is_valid()) { - ExtrusionPath path(role, mm3_per_mm, width, height); + ExtrusionPath path(attributes); path.polyline.points = std::move(poly.points); path.polyline.points.push_back(path.polyline.points.front()); dst.emplace_back(new ExtrusionLoop(std::move(path))); @@ -327,22 +401,14 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons loops.clear(); } -inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + polylines.size()); - for (Polyline &polyline : polylines) { - if (polyline.is_valid()) { - if (polyline.is_closed()) { - ExtrusionPath extrusion_path(role, mm3_per_mm, width, height); - extrusion_path.polyline = std::move(polyline); - dst.emplace_back(new ExtrusionLoop(std::move(extrusion_path))); - } else { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); - extrusion_path->polyline = std::move(polyline); - dst.emplace_back(extrusion_path); - } - } - } + for (Polyline &polyline : polylines) + if (polyline.is_valid()) + dst.emplace_back(polyline.is_closed() ? + static_cast(new ExtrusionLoop(ExtrusionPath{ std::move(polyline), attributes })) : + static_cast(new ExtrusionPath(std::move(polyline), attributes))); polylines.clear(); } diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 4e75a70617..7d5e46c977 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -14,6 +14,7 @@ namespace Slic3r { +#if 0 void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role) { if (role != ExtrusionRole::Mixed) { @@ -25,6 +26,7 @@ void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, last); } } +#endif ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths) : no_sort(false) @@ -91,18 +93,6 @@ void ExtrusionEntityCollection::remove(size_t i) this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role) -{ - // Return a filtered copy of the collection. - ExtrusionEntityCollection out; - out.entities = filter_by_extrusion_role(extrusion_entities, role); - // Clone the extrusion entities. - for (auto &ptr : out.entities) - ptr = ptr->clone(); - chain_and_reorder_extrusion_entities(out.entities, &start_near); - return out; -} - void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { for (const ExtrusionEntity *entity : this->entities) diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 6228c72989..477270adee 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -13,6 +13,7 @@ namespace Slic3r { +#if 0 // Remove those items from extrusion_entities, that do not match role. // Do nothing if role is mixed. // Removed elements are NOT being deleted. @@ -27,6 +28,7 @@ inline ExtrusionEntitiesPtr filter_by_extrusion_role(const ExtrusionEntitiesPtr filter_by_extrusion_role_in_place(out, role); return out; } +#endif class ExtrusionEntityCollection : public ExtrusionEntity { @@ -102,9 +104,6 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = ExtrusionRole::Mixed); - ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = ExtrusionRole::Mixed) const - { return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); } void reverse() override; const Point& first_point() const override { return this->entities.front()->first_point(); } const Point& last_point() const override { return this->entities.back()->last_point(); } diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index d092a95691..844c485f10 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -87,6 +87,7 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_external_perimeter() const { return this->is_perimeter() && this->is_external(); } bool is_infill() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Infill); } bool is_solid_infill() const { return this->is_infill() && this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } + bool is_sparse_infill() const { return this->is_infill() && ! this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } bool is_external() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::External); } bool is_bridge() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Bridge); } @@ -94,6 +95,9 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_support_base() const { return this->is_support() && ! this->is_external(); } bool is_support_interface() const { return this->is_support() && this->is_external(); } bool is_mixed() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Mixed); } + + // Brim is currently marked as skirt. + bool is_skirt() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Skirt); } }; // Special flags describing loop diff --git a/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp index 2d577c6d42..6408c4b144 100644 --- a/src/libslic3r/ExtrusionSimulator.cpp +++ b/src/libslic3r/ExtrusionSimulator.cpp @@ -961,9 +961,9 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const polyline.reserve(path.polyline.points.size()); float scalex = float(viewport.size().x()) / float(bbox.size().x()); float scaley = float(viewport.size().y()) / float(bbox.size().y()); - float w = scale_(path.width) * scalex; + float w = scale_(path.width()) * scalex; //float h = scale_(path.height) * scalex; - w = scale_(path.mm3_per_mm / path.height) * scalex; + w = scale_(path.mm3_per_mm() / path.height()) * scalex; // printf("scalex: %f, scaley: %f\n", scalex, scaley); // printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y); for (Points::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) { diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index f709fa715f..4f070140e8 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -543,19 +543,19 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: flow_mm3_per_mm = new_flow.mm3_per_mm(); flow_width = new_flow.width(); } - // Save into layer. - ExtrusionEntityCollection* eec = nullptr; - auto fill_begin = uint32_t(layerm.fills().size()); - layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection()); - // Only concentric fills are not sorted. - eec->no_sort = f->no_sort(); - if (params.use_arachne) { + auto fill_begin = uint32_t(layerm.fills().size()); + // Save into layer. + if (ExtrusionEntityCollection *eec = nullptr; params.use_arachne) { for (const ThickPolyline &thick_polyline : thick_polylines) { Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing)); ExtrusionMultiPath multi_path = PerimeterGenerator::thick_polyline_to_multi_path(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled(0.05), float(SCALED_EPSILON)); // Append paths to collection. if (!multi_path.empty()) { + layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection()); + // Only concentric fills are not sorted. + eec->no_sort = f->no_sort(); + if (multi_path.paths.front().first_point() == multi_path.paths.back().last_point()) eec->entities.emplace_back(new ExtrusionLoop(std::move(multi_path.paths))); else @@ -565,10 +565,15 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: thick_polylines.clear(); } else { + layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection()); + // Only concentric fills are not sorted. + eec->no_sort = f->no_sort(); + extrusion_entities_append_paths( eec->entities, std::move(polylines), - surface_fill.params.extrusion_role, - flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height()); + ExtrusionAttributes{ surface_fill.params.extrusion_role, + ExtrusionFlow{ flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height() } + }); } insert_fills_into_islands(*this, uint32_t(surface_fill.region_id), fill_begin, uint32_t(layerm.fills().size())); } @@ -913,8 +918,9 @@ void Layer::make_ironing() eec->no_sort = true; extrusion_entities_append_paths( eec->entities, std::move(polylines), - ExtrusionRole::Ironing, - flow_mm3_per_mm, extrusion_width, float(extrusion_height)); + ExtrusionAttributes{ ExtrusionRole::Ironing, + ExtrusionFlow{ flow_mm3_per_mm, extrusion_width, float(extrusion_height) } + }); insert_fills_into_islands(*this, ironing_params.region_id, fill_begin, uint32_t(ironing_params.layerm->fills().size())); } } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 74a40afd9f..1e17f8395d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -31,6 +31,7 @@ #include "GCode/PrintExtents.hpp" #include "GCode/Thumbnails.hpp" #include "GCode/WipeTower.hpp" +#include "GCode/WipeTowerIntegration.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" @@ -41,12 +42,13 @@ #include "ClipperUtils.hpp" #include "libslic3r.h" #include "LocalesUtils.hpp" -#include "libslic3r/format.hpp" +#include "format.hpp" #include #include #include #include +#include #include #include @@ -128,7 +130,7 @@ namespace Slic3r { return ok; } - std::string OozePrevention::pre_toolchange(GCode& gcodegen) + std::string OozePrevention::pre_toolchange(GCodeGenerator &gcodegen) { std::string gcode; @@ -154,315 +156,28 @@ namespace Slic3r { return gcode; } - std::string OozePrevention::post_toolchange(GCode& gcodegen) + std::string OozePrevention::post_toolchange(GCodeGenerator &gcodegen) { return (gcodegen.config().standby_temperature_delta.value != 0) ? gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) : std::string(); } - int OozePrevention::_get_temp(const GCode& gcodegen) const + int OozePrevention::_get_temp(const GCodeGenerator &gcodegen) const { - return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0) + // First layer temperature should be used when on the first layer (obviously) and when + // "other layers" is set to zero (which means it should not be used). + return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0 + || gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()) == 0) ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); } - std::string Wipe::wipe(GCode& gcodegen, bool toolchange) - { - std::string gcode; - const Extruder &extruder = *gcodegen.writer().extruder(); - - // Remaining quantized retraction length. - if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length()); - retract_length > 0 && this->path.size() >= 2) { - // Reduce feedrate a bit; travel speed is often too high to move on existing material. - // Too fast = ripping of existing material; too slow = short wipe path, thus more blob. - const double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8; - // Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one - // due to rounding (TODO: test and/or better math for this). - const double xy_to_e = 0.95 * extruder.retract_speed() / wipe_speed; - // Start with the current position, which may be different from the wipe path start in case of loop clipping. - Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos()); - auto it = this->path.points.begin(); - Vec2d p = gcodegen.point_to_gcode_quantized(*(++ it)); - if (p != prev) { - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; - auto end = this->path.points.end(); - bool done = false; - for (; it != end && ! done; ++ it) { - p = gcodegen.point_to_gcode_quantized(*it); - double segment_length = (p - prev).norm(); - double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); - if (dE > retract_length - EPSILON) { - if (dE > retract_length + EPSILON) - // Shorten the segment. - p = prev + (p - prev) * (retract_length / dE); - dE = retract_length; - done = true; - } - //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle. - // Is it here for the cooling markers? Or should it be outside of the cycle? - gcode += gcodegen.writer().set_speed(wipe_speed * 60, {}, gcodegen.enable_cooling_markers() ? ";_WIPE" : ""); - gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract"); - prev = p; - retract_length -= dE; - } - // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; - gcodegen.set_last_pos(gcodegen.gcode_to_point(prev)); - } - } - - // Prevent wiping again on the same path. - this->reset_path(); - return gcode; - } - - static inline Point wipe_tower_point_to_object_point(GCode& gcodegen, const Vec2f& wipe_tower_pt) - { - return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); - } - - std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const - { - if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) - throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); - - std::string gcode; - - // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) - // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position - float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); - - auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f { - Vec2f out = Eigen::Rotation2Df(alpha) * pt; - out += m_wipe_tower_pos; - return out; - }; - - Vec2f start_pos = tcr.start_pos; - Vec2f end_pos = tcr.end_pos; - if (! tcr.priming) { - start_pos = transform_wt_pt(start_pos); - end_pos = transform_wt_pt(end_pos); - } - - Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; - float wipe_tower_rotation = tcr.priming ? 0.f : alpha; - - std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); - - gcode += gcodegen.writer().unlift(); // Make sure there is no z-hop (in most cases, there isn't). - - double current_z = gcodegen.writer().get_position().z(); - if (z == -1.) // in case no specific z was provided, print at current_z pos - z = current_z; - - const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id); - const bool will_go_down = ! is_approx(z, current_z); - const bool is_ramming = (gcodegen.config().single_extruder_multi_material) - || (! gcodegen.config().single_extruder_multi_material && gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool)); - const bool should_travel_to_tower = ! tcr.priming - && (tcr.force_travel // wipe tower says so - || ! needs_toolchange // this is just finishing the tower with no toolchange - || is_ramming); - if (should_travel_to_tower) { - // FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges, - // then we could simplify the condition and make it more readable. - gcode += gcodegen.retract(); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); - gcode += gcodegen.travel_to( - wipe_tower_point_to_object_point(gcodegen, start_pos), - ExtrusionRole::Mixed, - "Travel to a Wipe Tower"); - gcode += gcodegen.unretract(); - } else { - // When this is multiextruder printer without any ramming, we can just change - // the tool without travelling to the tower. - } - - if (will_go_down) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); - gcode += gcodegen.writer().unretract(); - } - - std::string toolchange_gcode_str; - std::string deretraction_str; - if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) { - if (is_ramming) - gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines. - toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z - if (gcodegen.config().wipe_tower) - deretraction_str = gcodegen.unretract(); - } - - - - - // Insert the toolchange and deretraction gcode into the generated gcode. - DynamicConfig config; - config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); - config.set_key_value("deretraction_from_wipe_tower_generator", new ConfigOptionString(deretraction_str)); - std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); - unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); - gcode += tcr_gcode; - check_add_eol(toolchange_gcode_str); - - // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(end_pos.cast()); - gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); - if (!is_approx(z, current_z)) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); - gcode += gcodegen.writer().unretract(); - } - - else { - // Prepare a future wipe. - gcodegen.m_wipe.reset_path(); - for (const Vec2f& wipe_pt : tcr.wipe_path) - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt))); - } - - // Let the planner know we are traveling between objects. - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); - return gcode; - } - - // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode - // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) - std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const - { - Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); - - std::istringstream gcode_str(tcr.gcode); - std::string gcode_out; - std::string line; - Vec2f pos = tcr.start_pos; - Vec2f transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; - Vec2f old_pos(-1000.1f, -1000.1f); - - while (gcode_str) { - std::getline(gcode_str, line); // we read the gcode line by line - - // All G1 commands should be translated and rotated. X and Y coords are - // only pushed to the output when they differ from last time. - // WT generator can override this by appending the never_skip_tag - if (boost::starts_with(line, "G1 ")) { - bool never_skip = false; - auto it = line.find(WipeTower::never_skip_tag()); - if (it != std::string::npos) { - // remove the tag and remember we saw it - never_skip = true; - line.erase(it, it + WipeTower::never_skip_tag().size()); - } - std::ostringstream line_out; - std::istringstream line_str(line); - line_str >> std::noskipws; // don't skip whitespace - char ch = 0; - line_str >> ch >> ch; // read the "G1" - while (line_str >> ch) { - if (ch == 'X' || ch == 'Y') - line_str >> (ch == 'X' ? pos.x() : pos.y()); - else - line_out << ch; - } - - transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; - - if (transformed_pos != old_pos || never_skip) { - line = line_out.str(); - boost::trim_left(line); // Remove leading spaces - std::ostringstream oss; - oss << std::fixed << std::setprecision(3) << "G1"; - if (transformed_pos.x() != old_pos.x() || never_skip) - oss << " X" << transformed_pos.x() - extruder_offset.x(); - if (transformed_pos.y() != old_pos.y() || never_skip) - oss << " Y" << transformed_pos.y() - extruder_offset.y(); - if (! line.empty()) - oss << " "; - line = oss.str() + line; - old_pos = transformed_pos; - } - } - - gcode_out += line + "\n"; - - // If this was a toolchange command, we should change current extruder offset - if (line == "[toolchange_gcode]") { - extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); - - // If the extruder offset changed, add an extra move so everything is continuous - if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { - std::ostringstream oss; - oss << std::fixed << std::setprecision(3) - << "G1 X" << transformed_pos.x() - extruder_offset.x() - << " Y" << transformed_pos.y() - extruder_offset.y() - << "\n"; - gcode_out += oss.str(); - } - } - } - return gcode_out; - } - - - std::string WipeTowerIntegration::prime(GCode& gcodegen) - { - std::string gcode; - for (const WipeTower::ToolChangeResult& tcr : m_priming) { - if (! tcr.extrusions.empty()) - gcode += append_tcr(gcodegen, tcr, tcr.new_tool); - } - return gcode; - } - - std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer) - { - std::string gcode; - assert(m_layer_idx >= 0); - if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { - if (m_layer_idx < (int)m_tool_changes.size()) { - if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) - throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); - - // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, - // resulting in a wipe tower with sparse layers. - double wipe_tower_z = -1; - bool ignore_sparse = false; - if (gcodegen.config().wipe_tower_no_sparse_layers.value) { - wipe_tower_z = m_last_wipe_tower_print_z; - ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool && m_layer_idx != 0); - if (m_tool_change_idx == 0 && !ignore_sparse) - wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; - } - - if (!ignore_sparse) { - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); - m_last_wipe_tower_print_z = wipe_tower_z; - } - } - } - return gcode; - } - - // Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. - std::string WipeTowerIntegration::finalize(GCode& gcodegen) - { - std::string gcode; - if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) - gcode += gcodegen.change_layer(m_final_purge.print_z); - gcode += append_tcr(gcodegen, m_final_purge, -1); - return gcode; - } - const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) -void GCode::PlaceholderParserIntegration::reset() +void GCodeGenerator::PlaceholderParserIntegration::reset() { this->failed_templates.clear(); this->output_config.clear(); @@ -482,7 +197,7 @@ void GCode::PlaceholderParserIntegration::reset() this->e_restart_extra.clear(); } -void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) +void GCodeGenerator::PlaceholderParserIntegration::init(const GCodeWriter &writer) { this->reset(); const std::vector &extruders = writer.extruders(); @@ -518,7 +233,7 @@ void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) this->parser.set("zhop", this->opt_zhop); } -void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) +void GCodeGenerator::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) { memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3); this->opt_position->values = this->position; @@ -557,7 +272,7 @@ void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWri } // Throw if any of the output vector variables were resized by the script. -void GCode::PlaceholderParserIntegration::validate_output_vector_variables() +void GCodeGenerator::PlaceholderParserIntegration::validate_output_vector_variables() { if (this->opt_position->values.size() != 3) throw Slic3r::RuntimeError("\"position\" output variable must not be resized by the script."); @@ -573,9 +288,9 @@ void GCode::PlaceholderParserIntegration::validate_output_vector_variables() // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. -GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& object) +GCodeGenerator::ObjectsLayerToPrint GCodeGenerator::collect_layers_to_print(const PrintObject& object) { - GCode::ObjectsLayerToPrint layers_to_print; + GCodeGenerator::ObjectsLayerToPrint layers_to_print; layers_to_print.reserve(object.layers().size() + object.support_layers().size()); /* @@ -674,7 +389,7 @@ GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& obj // Prepare for non-sequential printing of multiple objects: Support resp. object layers with nearly identical print_z // will be printed for all objects at once. // Return a list of items. -std::vector> GCode::collect_layers_to_print(const Print& print) +std::vector> GCodeGenerator::collect_layers_to_print(const Print& print) { struct OrderingItem { coordf_t print_z; @@ -723,7 +438,7 @@ std::vector> GCode::collect_laye return layers_to_print; } -// free functions called by GCode::do_export() +// free functions called by GCodeGenerator::do_export() namespace DoExport { // static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) // { @@ -828,7 +543,7 @@ namespace DoExport { } } // namespace DoExport -void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) +void GCodeGenerator::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) { CNumericLocalesSetter locales_setter; @@ -866,6 +581,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); + m_processor.get_binary_data() = bgcode::binarize::BinaryData(); GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); @@ -923,7 +639,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu print->set_done(psGCodeExport); } -// free functions called by GCode::_do_export() +// free functions called by GCodeGenerator::_do_export() namespace DoExport { static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled) { @@ -1000,66 +716,74 @@ namespace DoExport { // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, - const WipeTowerData &wipe_tower_data, - const FullPrintConfig &config, - const std::vector &extruders, + const WipeTowerData &wipe_tower_data, + const FullPrintConfig &config, + const std::vector &extruders, unsigned int initial_extruder_id, - PrintStatistics &print_statistics) + PrintStatistics &print_statistics, + bool export_binary_data, + bgcode::binarize::BinaryData &binary_data) { - std::string filament_stats_string_out; + std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { - std::pair out_filament_used_mm ("; filament used [mm] = ", 0); - std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); - std::pair out_filament_used_g ("; filament used [g] = ", 0); - std::pair out_filament_cost ("; filament cost = ", 0); - for (const Extruder &extruder : extruders) { + if (! extruders.empty()) { + std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0); + std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0); + std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0); + std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0); + for (const Extruder &extruder : extruders) { print_statistics.printing_extruders.emplace_back(extruder.id()); filament_types.emplace_back(config.filament_type.get_at(extruder.id())); - double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter - double filament_weight = extruded_volume * extruder.filament_density() * 0.001; - double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { assert(is_decimal_separator_point()); - while (dst.second < extruder.id()) { - // Fill in the non-printing extruders with zeros. - dst.first += (dst.second > 0) ? ", 0" : "0"; - ++ dst.second; - } - if (dst.second > 0) - dst.first += ", "; - char buf[64]; - sprintf(buf, tmpl, value); - dst.first += buf; - ++ dst.second; - }; - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); - if (filament_weight > 0.) { - print_statistics.total_weight = print_statistics.total_weight + filament_weight; - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { - print_statistics.total_cost = print_statistics.total_cost + filament_cost; - append(out_filament_cost, "%.2lf", filament_cost); - } - } - print_statistics.total_used_filament += used_filament; - print_statistics.total_extruded_volume += extruded_volume; - print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; - print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; - } - filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; + while (dst.second < extruder.id()) { + // Fill in the non-printing extruders with zeros. + dst.first += (dst.second > 0) ? ", 0" : "0"; + ++ dst.second; + } + if (dst.second > 0) + dst.first += ", "; + char buf[64]; + sprintf(buf, tmpl, value); + dst.first += buf; + ++ dst.second; + }; + if (!export_binary_data) { + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); + } + if (filament_weight > 0.) { + print_statistics.total_weight = print_statistics.total_weight + filament_weight; + if (!export_binary_data) + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { + print_statistics.total_cost = print_statistics.total_cost + filament_cost; + if (!export_binary_data) + append(out_filament_cost, "%.2lf", filament_cost); + } + } + print_statistics.total_used_filament += used_filament; + print_statistics.total_extruded_volume += extruded_volume; + print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + } + if (!export_binary_data) { + filament_stats_string_out += out_filament_used_mm.first; + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; + } print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); std::sort(filament_types.begin(), filament_types.end()); print_statistics.printing_filament_types = filament_types.front(); @@ -1109,8 +833,130 @@ std::vector sort_object_instances_by_model_order(const Pri return instances; } -void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) +static inline bool arc_welder_enabled(const PrintConfig& print_config) { + return + // Enabled + print_config.arc_fitting != ArcFittingType::Disabled && + // Not a spiral vase print + !print_config.spiral_vase && + // Presure equalizer not used + print_config.max_volumetric_extrusion_rate_slope_negative == 0. && + print_config.max_volumetric_extrusion_rate_slope_positive == 0.; +} + +static inline GCode::SmoothPathCache::InterpolationParameters interpolation_parameters(const PrintConfig& print_config) +{ + return { + scaled(print_config.gcode_resolution.value), + arc_welder_enabled(print_config) ? Geometry::ArcWelder::default_arc_length_percent_tolerance : 0 + }; +} + +static inline GCode::SmoothPathCache smooth_path_interpolate_global(const Print& print) +{ + const GCode::SmoothPathCache::InterpolationParameters interpolation_params = interpolation_parameters(print.config()); + GCode::SmoothPathCache out; + out.interpolate_add(print.skirt(), interpolation_params); + out.interpolate_add(print.brim(), interpolation_params); + return out; +} + +void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) +{ + const bool export_to_binary_gcode = print.full_print_config().option("gcode_binary")->value; + // if exporting gcode in binary format: + // we generate here the data to be passed to the post-processor, who is responsible to export them to file + // 1) generate the thumbnails + // 2) collect the config data + if (export_to_binary_gcode) { + bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); + + // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // If "thumbnails_format" is not defined, export to PNG. + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + + if (!thumbnails.empty()) + GCodeThumbnails::generate_binary_thumbnails( + thumbnail_cb, binary_data.thumbnails, thumbnails, + [&print]() { print.throw_if_canceled(); }); + + // file data + binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); + + // config data + encode_full_config(print, binary_data.slicer_metadata.raw_data); + + // printer data + binary_data.printer_metadata.raw_data.emplace_back("printer_model", print.config().printer_model.value); // duplicated into config data + std::string filament_types_str; + for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { + filament_types_str += print.config().filament_type.values[i]; + if (i < print.config().filament_type.values.size() - 1) + filament_types_str += ";"; + } + binary_data.printer_metadata.raw_data.emplace_back("filament_type", filament_types_str); // duplicated into config data + char buf[1024]; + std::string nozzle_diameters_str; + for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { + sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2g," : "%.2g", print.config().nozzle_diameter.values[i]); + nozzle_diameters_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("nozzle_diameter", nozzle_diameters_str); // duplicated into config data + std::string bed_temperatures_str; + for (size_t i = 0; i < print.config().bed_temperature.values.size(); ++i) { + sprintf(buf, i < print.config().bed_temperature.values.size() - 1 ? "%d," : "%d", print.config().bed_temperature.values[i]); + bed_temperatures_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("bed_temperature", bed_temperatures_str); // duplicated into config data + + const DynamicPrintConfig& cfg = print.full_print_config(); + if (auto opt = cfg.option("brim_width"); opt != nullptr) { + sprintf(buf, "%.2g", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("brim_width", buf); // duplicated into config data + } + if (auto opt = cfg.option("fill_density"); opt != nullptr) { + sprintf(buf, "%.2g%%", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("fill_density", buf); // duplicated into config data + } + if (auto opt = cfg.option("layer_height"); opt != nullptr) { + sprintf(buf, "%.2g", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("layer_height", buf); // duplicated into config data + } + if (auto opt = cfg.option("temperature"); opt != nullptr) { + auto values = dynamic_cast(opt)->values; + std::string temperatures_str; + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%d," : "%d", values[i]); + temperatures_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("temperature", temperatures_str); // duplicated into config data + } + if (auto opt = cfg.option("ironing"); opt != nullptr) + binary_data.printer_metadata.raw_data.emplace_back("ironing", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data + if (auto opt = cfg.option("support_material"); opt != nullptr) + binary_data.printer_metadata.raw_data.emplace_back("support_material", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data + if (auto opt = cfg.option("extruder_colour"); opt != nullptr) { + auto values = dynamic_cast(opt)->values; + std::string extruder_colours_str; + if (values.size() == 1 && values.front().empty()) + extruder_colours_str = "\"\""; + else { + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%s;" : "%s", values[i].c_str()); + extruder_colours_str += buf; + } + } + binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data + } + } + // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -1164,19 +1010,25 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->m_avoid_crossing_curled_overhangs.init_bed_shape(get_bed_shape(print.config())); } - // Write information on the generator. - file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); + if (!export_to_binary_gcode) + // Write information on the generator. + file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); - // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". - // If "thumbnails_format" is not defined, export to PNG. - if (const auto [thumbnails, thumbnails_format] = std::make_pair( - print.full_print_config().option("thumbnails"), - print.full_print_config().option>("thumbnails_format")); - thumbnails) - GCodeThumbnails::export_thumbnails_to_file( - thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, - [&file](const char* sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); + if (! export_to_binary_gcode) { + // if exporting gcode in ascii format, generate the thumbnails here + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + + if (!thumbnails.empty()) + GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails, + [&file](const char* sz) { file.write(sz); }, + [&print]() { print.throw_if_canceled(); }); + } // Write notes (content of the Print Settings tab -> Notes) { @@ -1198,20 +1050,22 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato const double layer_height = first_object->config().layer_height.value; assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; - for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { - const PrintRegion ®ion = print.get_print_region(region_id); - file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); - file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); - file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); - file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); - file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); - if (print.has_support_material()) - file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); - if (print.config().first_layer_extrusion_width.value > 0) - file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); - file.write_format("\n"); + if (!export_to_binary_gcode) { + for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { + const PrintRegion ®ion = print.get_print_region(region_id); + file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); + file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); + file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); + file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); + file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); + if (print.has_support_material()) + file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); + if (print.config().first_layer_extrusion_width.value > 0) + file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); + file.write_format("\n"); + } + print.throw_if_canceled(); } - print.throw_if_canceled(); // adds tags for time estimators if (print.config().remaining_times.value) @@ -1306,6 +1160,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). { BoundingBoxf bbox(print.config().bed_shape.values); + assert(bbox.defined); + if (! bbox.defined) + // This should not happen, but let's make the compiler happy. + bbox.min = bbox.max = Vec2d::Zero(); this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); @@ -1369,6 +1227,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(this->set_extruder(initial_extruder_id, 0.)); } + GCode::SmoothPathCache smooth_path_cache_global = smooth_path_interpolate_global(print); + // Do all objects for each layer. if (print.config().complete_objects.value) { size_t finished_objects = 0; @@ -1413,7 +1273,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. - this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file); + this->process_layers(print, tool_ordering, collect_layers_to_print(object), + *print_object_instance_sequential_active - object.instances().data(), + smooth_path_cache_global, file); ++ finished_objects; // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. // Reset it when starting another object from 1st layer. @@ -1426,7 +1288,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato std::vector> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. if (has_wipe_tower && ! layers_to_print.empty()) { - m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); + m_wipe_tower = std::make_unique(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()); file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); if (print.config().single_extruder_multi_material_priming) { file.write(m_wipe_tower->prime(*this)); @@ -1469,7 +1331,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. - this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file); + this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, + smooth_path_cache_global, file); if (m_wipe_tower) // Purge the extruder, pull out the active filament. file.write(m_wipe_tower->finalize(*this)); @@ -1516,48 +1379,85 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.throw_if_canceled(); // Get filament stats. - file.write(DoExport::update_print_stats_and_format_filament_stats( - // Const inputs + const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats( + // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), m_writer.extruders(), initial_extruder_id, // Modifies - print.m_print_statistics)); - file.write("\n"); - file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); - file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); - if (print.m_print_statistics.total_toolchanges > 0) - file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); - file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + print.m_print_statistics, + export_to_binary_gcode, + m_processor.get_binary_data() + ); - // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. - // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. - { - file.write("\n; prusaslicer_config = begin\n"); - std::string full_config; - append_full_config(print, full_config); - if (!full_config.empty()) - file.write(full_config); - file.write("; prusaslicer_config = end\n"); + if (!export_to_binary_gcode) + file.write(filament_stats_string_out); + + if (export_to_binary_gcode) { + bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); + if (print.m_print_statistics.total_toolchanges > 0) + binary_data.print_metadata.raw_data.emplace_back("total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges)); + char buf[1024]; + sprintf(buf, "%.2lf", m_max_layer_z); + binary_data.printer_metadata.raw_data.emplace_back("max_layer_z", buf); + } + else { + // if exporting gcode in ascii format, statistics export is done here + file.write("\n"); + file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight); + file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost); + if (print.m_print_statistics.total_toolchanges > 0) + file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + + // if exporting gcode in ascii format, config export is done here + // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. + // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. + { + file.write("\n; prusaslicer_config = begin\n"); + std::string full_config; + append_full_config(print, full_config); + if (!full_config.empty()) + file.write(full_config); + file.write("; prusaslicer_config = end\n"); + } } print.throw_if_canceled(); } +// Fill in cache of smooth paths for perimeters, fills and supports of the given object layers. +// Based on params, the paths are either decimated to sparser polylines, or interpolated with circular arches. +void GCodeGenerator::smooth_path_interpolate( + const ObjectLayerToPrint &object_layer_to_print, + const GCode::SmoothPathCache::InterpolationParameters ¶ms, + GCode::SmoothPathCache &out) +{ + if (const Layer *layer = object_layer_to_print.object_layer; layer) { + for (const LayerRegion *layerm : layer->regions()) { + out.interpolate_add(layerm->perimeters(), params); + out.interpolate_add(layerm->fills(), params); + } + } + if (const SupportLayer *layer = object_layer_to_print.support_layer; layer) + out.interpolate_add(layer->support_fills, params); +} + // Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. -void GCode::process_layers( +void GCodeGenerator::process_layers( const Print &print, const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream) { - // The pipeline is variable: The vase mode filter is optional. size_t layer_to_print_idx = 0; - const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> LayerResult { + const GCode::SmoothPathCache::InterpolationParameters interpolation_params = interpolation_parameters(print.config()); + const auto smooth_path_interpolator = tbb::make_filter>(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &layers_to_print, &layer_to_print_idx, &interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { fc.stop(); @@ -1565,23 +1465,41 @@ void GCode::process_layers( } else { // Pressure equalizer need insert empty input. Because it returns one layer back. // Insert NOP (no operation) layer; - ++layer_to_print_idx; - return LayerResult::make_nop_layer_result(); + return { ++ layer_to_print_idx, {} }; } } else { - const std::pair &layer = layers_to_print[layer_to_print_idx++]; + print.throw_if_canceled(); + size_t idx = layer_to_print_idx ++; + GCode::SmoothPathCache smooth_path_cache; + for (const ObjectLayerToPrint &l : layers_to_print[idx].second) + GCodeGenerator::smooth_path_interpolate(l, interpolation_params, smooth_path_cache); + return { idx, std::move(smooth_path_cache) }; + } + }); + const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &smooth_path_cache_global]( + std::pair in) -> LayerResult { + size_t layer_to_print_idx = in.first; + if (layer_to_print_idx == layers_to_print.size()) { + // Pressure equalizer need insert empty input. Because it returns one layer back. + // Insert NOP (no operation) layer; + return LayerResult::make_nop_layer_result(); + } else { + const std::pair &layer = layers_to_print[layer_to_print_idx]; const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first); if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); print.throw_if_canceled(); - return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); + return this->process_layer(print, layer.second, layer_tools, + GCode::SmoothPathCaches{ smooth_path_cache_global, in.second }, + &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); } }); + // The pipeline is variable: The vase mode filter is optional. const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [spiral_vase = this->m_spiral_vase.get()](LayerResult in) -> LayerResult { if (in.nop_layer_result) return in; - spiral_vase->enable(in.spiral_vase_enable); return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; }); @@ -1604,45 +1522,40 @@ void GCode::process_layers( [&output_stream](std::string s) { output_stream.write(s); } ); + tbb::filter pipeline_to_layerresult = smooth_path_interpolator & generator; + if (m_spiral_vase) + pipeline_to_layerresult = pipeline_to_layerresult & spiral_vase; + if (m_pressure_equalizer) + pipeline_to_layerresult = pipeline_to_layerresult & pressure_equalizer; + + tbb::filter pipeline_to_string = cooling; + if (m_find_replace) + pipeline_to_string = pipeline_to_string & find_replace; + // It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline. // Handler is unregistered when the destructor is called. TBBLocalesSetter locales_setter; - // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); - if (m_spiral_vase && m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase && m_find_replace) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); - else if (m_spiral_vase && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output); - else if (m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); - else if (m_find_replace) - tbb::parallel_pipeline(12, generator & cooling & find_replace & output); - else if (m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output); - else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, pipeline_to_layerresult & pipeline_to_string & output); output_stream.find_replace_enable(); } // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. -void GCode::process_layers( +void GCodeGenerator::process_layers( const Print &print, const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream) { - // The pipeline is variable: The vase mode filter is optional. size_t layer_to_print_idx = 0; - const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> LayerResult { + const GCode::SmoothPathCache::InterpolationParameters interpolation_params = interpolation_parameters(print.config()); + const auto smooth_path_interpolator = tbb::make_filter> (slic3r_tbb_filtermode::serial_in_order, + [this, &print, &layers_to_print, &layer_to_print_idx, interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { fc.stop(); @@ -1650,15 +1563,32 @@ void GCode::process_layers( } else { // Pressure equalizer need insert empty input. Because it returns one layer back. // Insert NOP (no operation) layer; - ++layer_to_print_idx; - return LayerResult::make_nop_layer_result(); + return { ++ layer_to_print_idx, {} }; } } else { - ObjectLayerToPrint &layer = layers_to_print[layer_to_print_idx ++]; print.throw_if_canceled(); - return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx); + size_t idx = layer_to_print_idx ++; + GCode::SmoothPathCache smooth_path_cache; + GCodeGenerator::smooth_path_interpolate(layers_to_print[idx], interpolation_params, smooth_path_cache); + return { idx, std::move(smooth_path_cache) }; } }); + const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &tool_ordering, &layers_to_print, &smooth_path_cache_global, single_object_idx](std::pair in) -> LayerResult { + size_t layer_to_print_idx = in.first; + if (layer_to_print_idx == layers_to_print.size()) { + // Pressure equalizer need insert empty input. Because it returns one layer back. + // Insert NOP (no operation) layer; + return LayerResult::make_nop_layer_result(); + } else { + ObjectLayerToPrint &layer = layers_to_print[layer_to_print_idx]; + print.throw_if_canceled(); + return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), + GCode::SmoothPathCaches{ smooth_path_cache_global, in.second }, + &layer == &layers_to_print.back(), nullptr, single_object_idx); + } + }); + // The pipeline is variable: The vase mode filter is optional. const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [spiral_vase = this->m_spiral_vase.get()](LayerResult in)->LayerResult { if (in.nop_layer_result) @@ -1684,37 +1614,57 @@ void GCode::process_layers( [&output_stream](std::string s) { output_stream.write(s); } ); + tbb::filter pipeline_to_layerresult = smooth_path_interpolator & generator; + if (m_spiral_vase) + pipeline_to_layerresult = pipeline_to_layerresult & spiral_vase; + if (m_pressure_equalizer) + pipeline_to_layerresult = pipeline_to_layerresult & pressure_equalizer; + + tbb::filter pipeline_to_string = cooling; + if (m_find_replace) + pipeline_to_string = pipeline_to_string & find_replace; + // It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline. // Handler is unregistered when the destructor is called. TBBLocalesSetter locales_setter; - // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); - if (m_spiral_vase && m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase && m_find_replace) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); - else if (m_spiral_vase && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output); - else if (m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); - else if (m_find_replace) - tbb::parallel_pipeline(12, generator & cooling & find_replace & output); - else if (m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output); - else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, pipeline_to_layerresult & pipeline_to_string & output); output_stream.find_replace_enable(); } -std::string GCode::placeholder_parser_process( +std::string GCodeGenerator::placeholder_parser_process( const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) { +#ifndef NDEBUG // CHECK_CUSTOM_GCODE_PLACEHOLDERS + if (config_override) { + const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders(); + + // 1-st check: custom G-code "name" have to be present in s_CustomGcodeSpecificOptions; + //if (custom_gcode_placeholders.count(name) > 0) { + // const auto& placeholders = custom_gcode_placeholders.at(name); + if (auto it = custom_gcode_placeholders.find(name); it != custom_gcode_placeholders.end()) { + const auto& placeholders = it->second; + + for (const std::string& key : config_override->keys()) { + // 2-nd check: "key" have to be present in s_CustomGcodeSpecificOptions for "name" custom G-code ; + if (std::find(placeholders.begin(), placeholders.end(), key) == placeholders.end()) + throw Slic3r::PlaceholderParserError(format("\"%s\" placeholder for \"%s\" custom G-code \n" + "needs to be added to s_CustomGcodeSpecificOptions", key.c_str(), name.c_str())); + // 3-rd check: "key" have to be present in CustomGcodeSpecificConfigDef for "key" placeholder; + if (!custom_gcode_specific_config_def.has(key)) + throw Slic3r::PlaceholderParserError(format("Definition of \"%s\" placeholder \n" + "needs to be added to CustomGcodeSpecificConfigDef", key.c_str())); + } + } + else + throw Slic3r::PlaceholderParserError(format("\"%s\" custom G-code needs to be added to s_CustomGcodeSpecificOptions", name.c_str())); + } +#endif + PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { ppi.update_from_gcodewriter(m_writer); @@ -1823,7 +1773,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc // Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters. // Do not process this piece of G-code by the time estimator, it already knows the values through another sources. -void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) +void GCodeGenerator::print_machine_envelope(GCodeOutputStream &file, Print &print) { const GCodeFlavor flavor = print.config().gcode_flavor.value; if ( (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware) @@ -1884,7 +1834,7 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. // M140 - Set Extruder Temperature // M190 - Set Extruder Temperature and Wait -void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCodeGenerator::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { bool autoemit = print.config().autoemit_temperature_commands; // Initial bed temperature based on the first extruder. @@ -1906,7 +1856,7 @@ void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &p // M104 - Set Extruder Temperature // M109 - Set Extruder Temperature and Wait // RepRapFirmware: G10 Sxx -void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCodeGenerator::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { bool autoemit = print.config().autoemit_temperature_commands; // Is the bed temperature set by the provided custom G-code? @@ -1944,7 +1894,7 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr } } -std::vector GCode::sort_print_object_instances( +std::vector GCodeGenerator::sort_print_object_instances( const std::vector &object_layers, // Ordering must be defined for normal (non-sequential print). const std::vector *ordering, @@ -1985,7 +1935,7 @@ namespace ProcessLayer { static std::string emit_custom_gcode_per_print_z( - GCode &gcodegen, + GCodeGenerator &gcodegen, const CustomGCode::Item *custom_gcode, unsigned int current_extruder_id, // ID of the first extruder printing this layer. @@ -2138,11 +2088,12 @@ namespace Skirt { // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // and performing the extruder specific extrusions together. -LayerResult GCode::process_layer( +LayerResult GCodeGenerator::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, + const GCode::SmoothPathCaches &smooth_path_caches, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -2286,10 +2237,6 @@ LayerResult GCode::process_layer( } } - for (const ObjectLayerToPrint &layer_to_print : layers) { - m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.object_layer); - } - // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { @@ -2309,13 +2256,11 @@ LayerResult GCode::process_layer( double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. - ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); - for (ExtrusionPath &path : loop.paths) { - path.height = layer_skirt_flow.height(); - path.mm3_per_mm = mm3_per_mm; - } //FIXME using the support_material_speed of the 1st object printed. - gcode += this->extrude_loop(loop, "skirt"sv, m_config.support_material_speed.value); + gcode += this->extrude_skirt(dynamic_cast(*print.skirt().entities[i]), + // Override of skirt extrusion parameters. extrude_skirt() will fill in the extrusion width. + ExtrusionFlow{ mm3_per_mm, 0., layer_skirt_flow.height() }, + smooth_path_caches.global(), "skirt"sv, m_config.support_material_speed.value); } m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). @@ -2327,9 +2272,8 @@ LayerResult GCode::process_layer( if (! m_brim_done) { this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); - for (const ExtrusionEntity *ee : print.brim().entities) { - gcode += this->extrude_entity(*ee, "brim"sv, m_config.support_material_speed.value); - } + for (const ExtrusionEntity *ee : print.brim().entities) + gcode += this->extrude_entity({ *ee, false }, smooth_path_caches.global(), "brim"sv, m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point. @@ -2346,7 +2290,7 @@ LayerResult GCode::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_caches.layer_local(), is_anything_overridden, true /* print_wipe_extrusions */); if (gcode_size_old < gcode.size()) gcode+="; PURGING FINISHED\n"; @@ -2355,7 +2299,7 @@ LayerResult GCode::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_caches.layer_local(), is_anything_overridden, false /* print_wipe_extrusions */); } @@ -2373,7 +2317,7 @@ static inline bool comment_is_perimeter(const std::string_view comment) { return comment.data() == comment_perimeter.data() && comment.size() == comment_perimeter.size(); } -void GCode::process_layer_single_object( +void GCodeGenerator::process_layer_single_object( // output std::string &gcode, // Index of the extruder currently active. @@ -2384,6 +2328,8 @@ void GCode::process_layer_single_object( const ObjectLayerToPrint &layer_to_print, // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, + // Optional smooth path interpolating extrusion polylines. + const GCode::SmoothPathCache &smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). @@ -2422,8 +2368,6 @@ void GCode::process_layer_single_object( const PrintObject &print_object = print_instance.print_object; const Print &print = *print_object.print(); - m_extrusion_quality_estimator.set_current_object(&print_object); - if (! print_wipe_extrusions && layer_to_print.support_layer != nullptr) if (const SupportLayer &support_layer = *layer_to_print.support_layer; ! support_layer.support_fills.entities.empty()) { ExtrusionRole role = support_layer.support_fills.role(); @@ -2456,9 +2400,16 @@ void GCode::process_layer_single_object( init_layer_delayed(); m_layer = layer_to_print.support_layer; m_object_layer_over_raft = false; - gcode += this->extrude_support( - // support_extrusion_role is ExtrusionRole::SupportMaterial, ExtrusionRole::SupportMaterialInterface or ExtrusionRole::Mixed for all extrusion paths. - support_layer.support_fills.chained_path_from(m_last_pos, extrude_support ? (extrude_interface ? ExtrusionRole::Mixed : ExtrusionRole::SupportMaterial) : ExtrusionRole::SupportMaterialInterface)); + ExtrusionEntitiesPtr entities_cache; + const ExtrusionEntitiesPtr &entities = extrude_support && extrude_interface ? support_layer.support_fills.entities : entities_cache; + if (! extrude_support || ! extrude_interface) { + auto role = extrude_support ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface; + entities_cache.reserve(support_layer.support_fills.entities.size()); + for (ExtrusionEntity *ee : support_layer.support_fills.entities) + if (ee->role() == role) + entities_cache.emplace_back(ee); + } + gcode += this->extrude_support(chain_extrusion_references(entities), smooth_path_cache); } } @@ -2516,16 +2467,13 @@ void GCode::process_layer_single_object( if (! temp_fill_extrusions.empty()) { init_layer_delayed(); m_config.apply(region.config()); - //FIXME The source extrusions may be reversed, thus modifying the extrusions! Is it a problem? How about the initial G-code preview? - // Will parallel access of initial G-code preview to these extrusions while reordering them at backend cause issues? - chain_and_reorder_extrusion_entities(temp_fill_extrusions, &m_last_pos); const auto extrusion_name = ironing ? "ironing"sv : "infill"sv; - for (const ExtrusionEntity *fill : temp_fill_extrusions) - if (auto *eec = dynamic_cast(fill); eec) { - for (const ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) - gcode += this->extrude_entity(*ee, extrusion_name); + for (const ExtrusionEntityReference &fill : chain_extrusion_references(temp_fill_extrusions, &m_last_pos)) + if (auto *eec = dynamic_cast(&fill.extrusion_entity()); eec) { + for (const ExtrusionEntityReference &ee : chain_extrusion_references(*eec, &m_last_pos, fill.flipped())) + gcode += this->extrude_entity(ee, smooth_path_cache, extrusion_name); } else - gcode += this->extrude_entity(*fill, extrusion_name); + gcode += this->extrude_entity(fill, smooth_path_cache, extrusion_name); } }; @@ -2539,6 +2487,8 @@ void GCode::process_layer_single_object( const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); bool first = true; for (uint32_t perimeter_id : island.perimeters) { + // Extrusions inside islands are expected to be ordered already. + // Don't reorder them. assert(dynamic_cast(layerm.perimeters().entities[perimeter_id])); if (const auto *eec = static_cast(layerm.perimeters().entities[perimeter_id]); shall_print_this_extrusion_collection(eec, region)) { @@ -2550,7 +2500,8 @@ void GCode::process_layer_single_object( m_config.apply(region.config()); } for (const ExtrusionEntity *ee : *eec) - gcode += this->extrude_entity(*ee, comment_perimeter, -1.); + // Don't reorder, don't flip. + gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, comment_perimeter, -1.); } } }; @@ -2591,16 +2542,25 @@ void GCode::process_layer_single_object( gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; } -void GCode::apply_print_config(const PrintConfig &print_config) +void GCodeGenerator::apply_print_config(const PrintConfig &print_config) { m_writer.apply_print_config(print_config); m_config.apply(print_config); m_scaled_resolution = scaled(print_config.gcode_resolution.value); } -void GCode::append_full_config(const Print &print, std::string &str) +void GCodeGenerator::append_full_config(const Print &print, std::string &str) { - const DynamicPrintConfig &cfg = print.full_print_config(); + std::vector> config; + encode_full_config(print, config); + for (const auto& [key, value] : config) { + str += "; " + key + " = " + value + "\n"; + } +} + +void GCodeGenerator::encode_full_config(const Print& print, std::vector>& config) +{ + const DynamicPrintConfig& cfg = print.full_print_config(); // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. static constexpr auto banned_keys = { "compatible_printers"sv, @@ -2611,40 +2571,33 @@ void GCode::append_full_config(const Print &print, std::string &str) "printhost_cafile"sv }; assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); - auto is_banned = [](const std::string &key) { + auto is_banned = [](const std::string& key) { return std::binary_search(banned_keys.begin(), banned_keys.end(), key); }; - for (const std::string &key : cfg.keys()) - if (! is_banned(key) && ! cfg.option(key)->is_nil()) - str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; + config.reserve(config.size() + cfg.keys().size()); + for (const std::string& key : cfg.keys()) { + if (!is_banned(key) && !cfg.option(key)->is_nil()) + config.emplace_back(key, cfg.opt_serialize(key)); + } + config.shrink_to_fit(); } -void GCode::set_extruders(const std::vector &extruder_ids) +void GCodeGenerator::set_extruders(const std::vector &extruder_ids) { m_writer.set_extruders(extruder_ids); - - // enable wipe path generation if any extruder has wipe enabled - m_wipe.enable = false; - for (auto id : extruder_ids) - if (m_config.wipe.get_at(id)) { - m_wipe.enable = true; - break; - } + m_wipe.init(this->config(), extruder_ids); } -void GCode::set_origin(const Vec2d &pointf) +void GCodeGenerator::set_origin(const Vec2d &pointf) { // if origin increases (goes towards right), last_pos decreases because it goes towards left - const Point translate( - scale_(m_origin(0) - pointf(0)), - scale_(m_origin(1) - pointf(1)) - ); - m_last_pos += translate; - m_wipe.path.translate(translate); + const auto offset = Point::new_scale(m_origin - pointf); + m_last_pos += offset; + m_wipe.offset_path(offset); m_origin = pointf; } -std::string GCode::preamble() +std::string GCodeGenerator::preamble() { std::string gcode = m_writer.preamble(); @@ -2657,8 +2610,8 @@ std::string GCode::preamble() return gcode; } -// called by GCode::process_layer() -std::string GCode::change_layer(coordf_t print_z) +// called by GCodeGenerator::process_layer() +std::string GCodeGenerator::change_layer(coordf_t print_z) { std::string gcode; if (m_layer_count > 0) @@ -2680,198 +2633,180 @@ std::string GCode::change_layer(coordf_t print_z) return gcode; } -std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view description, double speed) +#ifndef NDEBUG +static inline bool validate_smooth_path(const GCode::SmoothPath &smooth_path, bool loop) { - // get a copy; don't modify the orientation of the original loop object otherwise - // next copies (if any) would not detect the correct orientation - - // extrude all loops ccw - bool was_clockwise = loop.make_counter_clockwise(); - - // find the point of the loop that is closest to the current extruder position - // or randomize if requested - Point last_pos = this->last_pos(); + for (auto it = std::next(smooth_path.begin()); it != smooth_path.end(); ++ it) { + assert(it->path.size() >= 2); + assert(std::prev(it)->path.back().point == it->path.front().point); + } + assert(! loop || smooth_path.front().path.front().point == smooth_path.back().path.back().point); + return true; +} +#endif //NDEBUG +std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) +{ + // Extrude all loops CCW. + bool is_hole = loop_src.is_clockwise(); + Point seam_point = this->last_pos(); if (! m_config.spiral_vase && comment_is_perimeter(description)) { assert(m_layer != nullptr); - m_seam_placer.place_seam(m_layer, loop, m_config.external_perimeters_first, this->last_pos()); - } else - // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, - // thus empty path segments will not be produced by G-code export. - loop.split_at(last_pos, false, scaled(0.0015)); - - for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) { - assert(it->polyline.points.size() >= 2); - assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + seam_point = m_seam_placer.place_seam(m_layer, loop_src, m_config.external_perimeters_first, this->last_pos()); } - assert(loop.paths.front().first_point() == loop.paths.back().last_point()); + // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, + // thus empty path segments will not be produced by G-code export. + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( + loop_src, is_hole, m_scaled_resolution, seam_point, scaled(0.0015)); - // clip the path to avoid the extruder to get exactly on the first point of the loop; + // Clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so - // we discard it in that case - double clip_length = m_enable_loop_clipping ? - scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER : - 0; + // we discard it in that case. + if (m_enable_loop_clipping) + clip_end(smooth_path, scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); - // get paths - ExtrusionPaths paths; - loop.clip_end(clip_length, &paths); - if (paths.empty()) return ""; + if (smooth_path.empty()) + return {}; - // apply the small perimeter speed - if (paths.front().role().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1) + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); + + // Apply the small perimeter speed. + if (loop_src.paths.front().role().is_perimeter() && loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1) speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); - // extrude along the path + // Extrude along the smooth path. std::string gcode; - for (ExtrusionPath &path : paths) { - path.simplify(m_scaled_resolution); - gcode += this->_extrude(path, description, speed); - } + for (const GCode::SmoothPathElement &el : smooth_path) + gcode += this->_extrude(el.path_attributes, el.path, description, speed); // reset acceleration - gcode += m_writer.set_print_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5)); + gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); - if (m_wipe.enable) { - m_wipe.path = paths.front().polyline; - - for (auto it = std::next(paths.begin()); it != paths.end(); ++it) { - if (it->role().is_bridge()) - break; // Don't perform a wipe on bridges. - - assert(it->polyline.points.size() >= 2); - assert(m_wipe.path.points.back() == it->polyline.first_point()); - if (m_wipe.path.points.back() != it->polyline.first_point()) - break; // ExtrusionLoop is interrupted in some place. - - m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.begin() + 1, it->polyline.points.end()); - } - } - - // make a little move inwards before leaving loop - if (paths.back().role().is_external_perimeter() && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { - // detect angle between last and first segment - // the side depends on the original winding order of the polygon (left for contours, right for holes) - //FIXME improve the algorithm in case the loop is tiny. - //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). - // Angle from the 2nd point to the last point. - double angle_inside = angle(paths.front().polyline.points[1] - paths.front().first_point(), - *(paths.back().polyline.points.end()-3) - paths.front().first_point()); - assert(angle_inside >= -M_PI && angle_inside <= M_PI); - // 3rd of this angle will be taken, thus make the angle monotonic before interpolation. - if (was_clockwise) { - if (angle_inside > 0) - angle_inside -= 2.0 * M_PI; - } else { - if (angle_inside < 0) - angle_inside += 2.0 * M_PI; - } - - // create the destination point along the first segment and rotate it - // we make sure we don't exceed the segment length because we don't know - // the rotation of the second segment so we might cross the object boundary - Vec2d p1 = paths.front().polyline.points.front().cast(); - Vec2d p2 = paths.front().polyline.points[1].cast(); - Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); - double l2 = v.squaredNorm(); - // Shift by no more than a nozzle diameter. - //FIXME Hiding the seams will not work nicely for very densely discretized contours! - Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); - // Rotate pt inside around the seam point. - pt.rotate(angle_inside / 3., paths.front().polyline.points.front()); - // generate the travel move - gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel"); + if (m_wipe.enabled()) { + // Wipe will hide the seam. + m_wipe.set_path(std::move(smooth_path), false); + } else if (loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) { + // Only wipe inside if the wipe along the perimeter is disabled. + // Make a little move inwards before leaving loop. + if (std::optional pt = wipe_hide_seam(smooth_path, is_hole, scale_(EXTRUDER_CONFIG(nozzle_diameter))); pt) + // Generate the seam hiding travel move. + gcode += m_writer.travel_to_xy(this->point_to_gcode(*pt), "move inwards before travel"); } return gcode; } -std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_skirt( + const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, + const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { - for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) { + assert(loop_src.is_counter_clockwise()); + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( + loop_src, false, m_scaled_resolution, this->last_pos(), scaled(0.0015)); + + // Clip the path to avoid the extruder to get exactly on the first point of the loop; + // if polyline was shorter than the clipping distance we'd get a null polyline, so + // we discard it in that case. + if (m_enable_loop_clipping) + clip_end(smooth_path, scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); + + if (smooth_path.empty()) + return {}; + + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); + + // Extrude along the smooth path. + std::string gcode; + for (GCode::SmoothPathElement &el : smooth_path) { + // Override extrusion parameters. + el.path_attributes.mm3_per_mm = extrusion_flow_override.mm3_per_mm; + el.path_attributes.height = extrusion_flow_override.height; + gcode += this->_extrude(el.path_attributes, el.path, description, speed); + } + + // reset acceleration + gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); + + if (m_wipe.enabled()) + // Wipe will hide the seam. + m_wipe.set_path(std::move(smooth_path), false); + + return gcode; +} + +std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) +{ +#ifndef NDEBUG + for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++ it) { assert(it->polyline.points.size() >= 2); assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } +#endif // NDEBUG + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit(multipath, reverse, m_scaled_resolution); + // extrude along the path std::string gcode; - for (ExtrusionPath path : multipath.paths) { - path.simplify(m_scaled_resolution); - gcode += this->_extrude(path, description, speed); - } - if (m_wipe.enable) { - m_wipe.path = std::move(multipath.paths.back().polyline); - m_wipe.path.reverse(); - - for (auto it = std::next(multipath.paths.rbegin()); it != multipath.paths.rend(); ++it) { - if (it->role().is_bridge()) - break; // Do not perform a wipe on bridges. - - assert(it->polyline.points.size() >= 2); - assert(m_wipe.path.points.back() == it->polyline.last_point()); - if (m_wipe.path.points.back() != it->polyline.last_point()) - break; // ExtrusionMultiPath is interrupted in some place. - - m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.rbegin() + 1, it->polyline.points.rend()); - } - } + for (GCode::SmoothPathElement &el : smooth_path) + gcode += this->_extrude(el.path_attributes, el.path, description, speed); + m_wipe.set_path(std::move(smooth_path), true); // reset acceleration gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; } -std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { - if (const ExtrusionPath* path = dynamic_cast(&entity)) - return this->extrude_path(*path, description, speed); - else if (const ExtrusionMultiPath* multipath = dynamic_cast(&entity)) - return this->extrude_multi_path(*multipath, description, speed); - else if (const ExtrusionLoop* loop = dynamic_cast(&entity)) - return this->extrude_loop(*loop, description, speed); + if (const ExtrusionPath *path = dynamic_cast(&entity.extrusion_entity())) + return this->extrude_path(*path, entity.flipped(), smooth_path_cache, description, speed); + else if (const ExtrusionMultiPath *multipath = dynamic_cast(&entity.extrusion_entity())) + return this->extrude_multi_path(*multipath, entity.flipped(), smooth_path_cache, description, speed); + else if (const ExtrusionLoop *loop = dynamic_cast(&entity.extrusion_entity())) + return this->extrude_loop(*loop, smooth_path_cache, description, speed); else throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()"); - return ""; + return {}; } -std::string GCode::extrude_path(ExtrusionPath path, std::string_view description, double speed) +std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, std::string_view description, double speed) { - path.simplify(m_scaled_resolution); - std::string gcode = this->_extrude(path, description, speed); - if (m_wipe.enable) { - m_wipe.path = std::move(path.polyline); - m_wipe.path.reverse(); - } + Geometry::ArcWelder::Path smooth_path = smooth_path_cache.resolve_or_fit(path, reverse, m_scaled_resolution); + std::string gcode = this->_extrude(path.attributes(), smooth_path, description, speed); + Geometry::ArcWelder::reverse(smooth_path); + m_wipe.set_path(std::move(smooth_path)); // reset acceleration gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; } -std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fills) +std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache) { static constexpr const auto support_label = "support material"sv; static constexpr const auto support_interface_label = "support material interface"sv; std::string gcode; - if (! support_fills.entities.empty()) { + if (! support_fills.empty()) { const double support_speed = m_config.support_material_speed.value; const double support_interface_speed = m_config.support_material_interface_speed.get_abs_value(support_speed); - for (const ExtrusionEntity *ee : support_fills.entities) { - ExtrusionRole role = ee->role(); + for (const ExtrusionEntityReference &eref : support_fills) { + ExtrusionRole role = eref.extrusion_entity().role(); assert(role == ExtrusionRole::SupportMaterial || role == ExtrusionRole::SupportMaterialInterface); const auto label = (role == ExtrusionRole::SupportMaterial) ? support_label : support_interface_label; const double speed = (role == ExtrusionRole::SupportMaterial) ? support_speed : support_interface_speed; - const ExtrusionPath *path = dynamic_cast(ee); + const ExtrusionPath *path = dynamic_cast(&eref.extrusion_entity()); if (path) - gcode += this->extrude_path(*path, label, speed); + gcode += this->extrude_path(*path, eref.flipped(), smooth_path_cache, label, speed); + else if (const ExtrusionMultiPath *multipath = dynamic_cast(&eref.extrusion_entity()); multipath) + gcode += this->extrude_multi_path(*multipath, eref.flipped(), smooth_path_cache, label, speed); else { - const ExtrusionMultiPath *multipath = dynamic_cast(ee); - if (multipath) - gcode += this->extrude_multi_path(*multipath, label, speed); - else { - const ExtrusionEntityCollection *eec = dynamic_cast(ee); - assert(eec); - if (eec) - gcode += this->extrude_support(*eec); + const ExtrusionEntityCollection *eec = dynamic_cast(&eref.extrusion_entity()); + assert(eec); + if (eec) { + //FIXME maybe order the support here? + ExtrusionEntityReferences refs; + refs.reserve(eec->entities.size()); + std::transform(eec->entities.begin(), eec->entities.end(), std::back_inserter(refs), + [flipped = eref.flipped()](const ExtrusionEntity *ee) { return ExtrusionEntityReference{ *ee, flipped }; }); + gcode += this->extrude_support(refs, smooth_path_cache); } } } @@ -2879,17 +2814,17 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill return gcode; } -bool GCode::GCodeOutputStream::is_error() const +bool GCodeGenerator::GCodeOutputStream::is_error() const { return ::ferror(this->f); } -void GCode::GCodeOutputStream::flush() +void GCodeGenerator::GCodeOutputStream::flush() { ::fflush(this->f); } -void GCode::GCodeOutputStream::close() +void GCodeGenerator::GCodeOutputStream::close() { if (this->f) { ::fclose(this->f); @@ -2897,7 +2832,7 @@ void GCode::GCodeOutputStream::close() } } -void GCode::GCodeOutputStream::write(const char *what) +void GCodeGenerator::GCodeOutputStream::write(const char *what) { if (what != nullptr) { //FIXME don't allocate a string, maybe process a batch of lines? @@ -2908,13 +2843,13 @@ void GCode::GCodeOutputStream::write(const char *what) } } -void GCode::GCodeOutputStream::writeln(const std::string &what) +void GCodeGenerator::GCodeOutputStream::writeln(const std::string &what) { if (! what.empty()) this->write(what.back() == '\n' ? what : what + '\n'); } -void GCode::GCodeOutputStream::write_format(const char* format, ...) +void GCodeGenerator::GCodeOutputStream::write_format(const char* format, ...) { va_list args; va_start(args, format); @@ -2946,18 +2881,22 @@ void GCode::GCodeOutputStream::write_format(const char* format, ...) va_end(args); } -std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view description, double speed) +std::string GCodeGenerator::_extrude( + const ExtrusionAttributes &path_attr, + const Geometry::ArcWelder::Path &path, + const std::string_view description, + double speed) { std::string gcode; - const std::string_view description_bridge = path.role().is_bridge() ? " (bridge)"sv : ""sv; + const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; // go to first point of extrusion path - if (!m_last_pos_defined || m_last_pos != path.first_point()) { + if (!m_last_pos_defined || m_last_pos != path.front().point) { std::string comment = "move to first "; comment += description; comment += description_bridge; comment += " point"; - gcode += this->travel_to(path.first_point(), path.role(), comment); + gcode += this->travel_to(path.front().point, path_attr.role, comment); } // compensate retraction @@ -2970,17 +2909,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de acceleration = m_config.first_layer_acceleration.value; } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) { acceleration = m_config.first_layer_acceleration_over_raft.value; - } else if (m_config.bridge_acceleration.value > 0 && path.role().is_bridge()) { + } else if (m_config.bridge_acceleration.value > 0 && path_attr.role.is_bridge()) { acceleration = m_config.bridge_acceleration.value; - } else if (m_config.top_solid_infill_acceleration > 0 && path.role() == ExtrusionRole::TopSolidInfill) { + } else if (m_config.top_solid_infill_acceleration > 0 && path_attr.role == ExtrusionRole::TopSolidInfill) { acceleration = m_config.top_solid_infill_acceleration.value; - } else if (m_config.solid_infill_acceleration > 0 && path.role().is_solid_infill()) { + } else if (m_config.solid_infill_acceleration > 0 && path_attr.role.is_solid_infill()) { acceleration = m_config.solid_infill_acceleration.value; - } else if (m_config.infill_acceleration.value > 0 && path.role().is_infill()) { + } else if (m_config.infill_acceleration.value > 0 && path_attr.role.is_infill()) { acceleration = m_config.infill_acceleration.value; - } else if (m_config.external_perimeter_acceleration > 0 && path.role().is_external_perimeter()) { + } else if (m_config.external_perimeter_acceleration > 0 && path_attr.role.is_external_perimeter()) { acceleration = m_config.external_perimeter_acceleration.value; - } else if (m_config.perimeter_acceleration.value > 0 && path.role().is_perimeter()) { + } else if (m_config.perimeter_acceleration.value > 0 && path_attr.role.is_perimeter()) { acceleration = m_config.perimeter_acceleration.value; } else { acceleration = m_config.default_acceleration.value; @@ -2989,36 +2928,36 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } // calculate extrusion length per distance unit - double e_per_mm = m_writer.extruder()->e_per_mm3() * path.mm3_per_mm; + double e_per_mm = m_writer.extruder()->e_per_mm3() * path_attr.mm3_per_mm; if (m_writer.extrusion_axis().empty()) // gcfNoExtrusion e_per_mm = 0; // set speed if (speed == -1) { - if (path.role() == ExtrusionRole::Perimeter) { + if (path_attr.role == ExtrusionRole::Perimeter) { speed = m_config.get_abs_value("perimeter_speed"); - } else if (path.role() == ExtrusionRole::ExternalPerimeter) { + } else if (path_attr.role == ExtrusionRole::ExternalPerimeter) { speed = m_config.get_abs_value("external_perimeter_speed"); - } else if (path.role().is_bridge()) { - assert(path.role().is_perimeter() || path.role() == ExtrusionRole::BridgeInfill); + } else if (path_attr.role.is_bridge()) { + assert(path_attr.role.is_perimeter() || path_attr.role == ExtrusionRole::BridgeInfill); speed = m_config.get_abs_value("bridge_speed"); - } else if (path.role() == ExtrusionRole::InternalInfill) { + } else if (path_attr.role == ExtrusionRole::InternalInfill) { speed = m_config.get_abs_value("infill_speed"); - } else if (path.role() == ExtrusionRole::SolidInfill) { + } else if (path_attr.role == ExtrusionRole::SolidInfill) { speed = m_config.get_abs_value("solid_infill_speed"); - } else if (path.role() == ExtrusionRole::TopSolidInfill) { + } else if (path_attr.role == ExtrusionRole::TopSolidInfill) { speed = m_config.get_abs_value("top_solid_infill_speed"); - } else if (path.role() == ExtrusionRole::Ironing) { + } else if (path_attr.role == ExtrusionRole::Ironing) { speed = m_config.get_abs_value("ironing_speed"); - } else if (path.role() == ExtrusionRole::GapFill) { + } else if (path_attr.role == ExtrusionRole::GapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { throw Slic3r::InvalidArgument("Invalid speed"); } } if (m_volumetric_speed != 0. && speed == 0) - speed = m_volumetric_speed / path.mm3_per_mm; + speed = m_volumetric_speed / path_attr.mm3_per_mm; if (this->on_first_layer()) speed = m_config.get_abs_value("first_layer_speed", speed); else if (this->object_layer_over_raft()) @@ -3027,54 +2966,36 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, - m_config.max_volumetric_speed.value / path.mm3_per_mm + m_config.max_volumetric_speed.value / path_attr.mm3_per_mm ); } if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, - EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm + EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm ); } - bool variable_speed_or_fan_speed = false; - std::vector new_points{}; - if ((this->m_config.enable_dynamic_overhang_speeds || this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) && - !this->on_first_layer() && path.role().is_perimeter()) { - std::vector> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}}; - if (this->m_config.enable_dynamic_overhang_speeds) { - overhangs_with_speeds = {{0, m_config.overhang_speed_0}, - {25, m_config.overhang_speed_1}, - {50, m_config.overhang_speed_2}, - {75, m_config.overhang_speed_3}, - {100, ConfigOptionFloatOrPercent{speed, false}}}; - } - - std::vector> overhang_w_fan_speeds = {{100, ConfigOptionInts{0}}}; - if (this->m_config.enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) { - overhang_w_fan_speeds = {{0, m_config.overhang_fan_speed_0}, - {25, m_config.overhang_fan_speed_1}, - {50, m_config.overhang_fan_speed_2}, - {75, m_config.overhang_fan_speed_3}, - {100, ConfigOptionInts{0}}}; - } - + std::pair dynamic_speed_and_fan_speed{-1, -1}; + if (path_attr.overhang_attributes.has_value()) { double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); if (external_perim_reference_speed == 0) - external_perim_reference_speed = m_volumetric_speed / path.mm3_per_mm; + external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; if (m_config.max_volumetric_speed.value > 0) - external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path.mm3_per_mm); + external_perim_reference_speed = std::min(external_perim_reference_speed, + m_config.max_volumetric_speed.value / path_attr.mm3_per_mm); if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { external_perim_reference_speed = std::min(external_perim_reference_speed, - EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); + EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm); } - new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds, - m_writer.extruder()->id(), external_perim_reference_speed, - speed); - variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), - [speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; }); + dynamic_speed_and_fan_speed = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(), + external_perim_reference_speed, speed); + } + + if (dynamic_speed_and_fan_speed.first > -1) { + speed = dynamic_speed_and_fan_speed.first; } double F = speed * 60; // convert mm/sec to mm/min @@ -3082,7 +3003,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // extrude arc or line if (m_enable_extrusion_role_markers) { - if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_extrusion_role) + if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path_attr.role); role != m_last_extrusion_role) { m_last_extrusion_role = role; if (m_enable_extrusion_role_markers) @@ -3100,29 +3021,29 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de bool last_was_wipe_tower = (m_last_processor_extrusion_role == GCodeExtrusionRole::WipeTower); assert(is_decimal_separator_point()); - if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_processor_extrusion_role) { + if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path_attr.role); role != m_last_processor_extrusion_role) { m_last_processor_extrusion_role = role; char buf[64]; sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(m_last_processor_extrusion_role).c_str()); gcode += buf; } - if (last_was_wipe_tower || m_last_width != path.width) { - m_last_width = path.width; + if (last_was_wipe_tower || m_last_width != path_attr.width) { + m_last_width = path_attr.width; gcode += std::string(";") + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) + float_to_string_decimal_point(m_last_width) + "\n"; } #if ENABLE_GCODE_VIEWER_DATA_CHECKING - if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { - m_last_mm3_per_mm = path.mm3_per_mm; + if (last_was_wipe_tower || (m_last_mm3_per_mm != path_attr.mm3_per_mm)) { + m_last_mm3_per_mm = path_attr.mm3_per_mm; gcode += std::string(";") + GCodeProcessor::Mm3_Per_Mm_Tag + float_to_string_decimal_point(m_last_mm3_per_mm) + "\n"; } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) { - m_last_height = path.height; + if (last_was_wipe_tower || std::abs(m_last_height - path_attr.height) > EPSILON) { + m_last_height = path_attr.height; gcode += std::string(";") + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + float_to_string_decimal_point(m_last_height) + "\n"; @@ -3130,72 +3051,85 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de std::string cooling_marker_setspeed_comments; if (m_enable_cooling_markers) { - if (path.role().is_bridge()) + if (path_attr.role.is_bridge()) gcode += ";_BRIDGE_FAN_START\n"; else cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED"; - if (path.role() == ExtrusionRole::ExternalPerimeter) + if (path_attr.role == ExtrusionRole::ExternalPerimeter) cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER"; } - if (!variable_speed_or_fan_speed) { - // F is mm per minute. - gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); - double path_length = 0.; - std::string comment; - if (m_config.gcode_comments) { - comment = description; - comment += description_bridge; + // F is mm per minute. + gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); + if (dynamic_speed_and_fan_speed.second >= 0) + gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_speed_and_fan_speed.second)) + "\n"; + double path_length = 0.; + std::string comment; + if (m_config.gcode_comments) { + comment = description; + comment += description_bridge; + } + Vec2d prev = this->point_to_gcode_quantized(path.front().point); + auto it = path.begin(); + auto end = path.end(); + const bool emit_radius = m_config.arc_fitting == ArcFittingType::EmitRadius; + for (++ it; it != end; ++ it) { + Vec2d p = this->point_to_gcode_quantized(it->point); + // Center of the radius to be emitted into the G-code: Either by radius or by center offset. + double radius = 0; + Vec2d ij; + if (it->radius != 0) { + // Extrude an arc. + assert(m_config.arc_fitting == ArcFittingType::EmitCenter || + m_config.arc_fitting == ArcFittingType::EmitRadius); + radius = unscaled(it->radius); + if (emit_radius) { + // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. + radius = GCodeFormatter::quantize_xyzf(radius); + } else { + // Calculate quantized IJ circle center offset. + ij = GCodeFormatter::quantize(Vec2d( + Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) + - prev)); + if (ij == Vec2d::Zero()) + // Don't extrude a degenerated circle. + radius = 0; + } } - Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front()); - auto it = path.polyline.points.begin(); - auto end = path.polyline.points.end(); - for (++ it; it != end; ++ it) { - Vec2d p = this->point_to_gcode_quantized(*it); - const double line_length = (p - prev).norm(); + if (radius == 0) { + // Extrude line segment. + if (const double line_length = (p - prev).norm(); line_length > 0) { + path_length += line_length; + gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + } + } else { + double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); + assert(angle > 0); + const double line_length = angle * std::abs(radius); path_length += line_length; - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); - prev = p; + const double dE = e_per_mm * line_length; + assert(dE > 0); + gcode += emit_radius ? + m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), dE, comment) : + m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); } - } else { - std::string marked_comment; - if (m_config.gcode_comments) { - marked_comment = description; - marked_comment += description_bridge; - } - double last_set_speed = new_points[0].speed * 60.0; - double last_set_fan_speed = new_points[0].fan_speed; - gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments); - gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n"; - Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); - for (size_t i = 1; i < new_points.size(); i++) { - const ProcessedPoint &processed_point = new_points[i]; - Vec2d p = this->point_to_gcode_quantized(processed_point.p); - const double line_length = (p - prev).norm(); - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, marked_comment); - prev = p; - double new_speed = processed_point.speed * 60.0; - if (last_set_speed != new_speed) { - gcode += m_writer.set_speed(new_speed, "", cooling_marker_setspeed_comments); - last_set_speed = new_speed; - } - if (last_set_fan_speed != processed_point.fan_speed) { - last_set_fan_speed = processed_point.fan_speed; - gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n"; - } - } - gcode += "\n;_RESET_FAN_SPEED\n"; + prev = p; } if (m_enable_cooling_markers) - gcode += path.role().is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END" : ";_EXTRUDE_END"; - this->set_last_pos(path.last_point()); + if (dynamic_speed_and_fan_speed.second >= 0) + gcode += ";_RESET_FAN_SPEED"; + + gcode += "\n"; + + this->set_last_pos(path.back().point); return gcode; } // This method accepts &point in print coordinates. -std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) +std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, std::string comment) { /* Define the travel move as a line between current position and the taget point. This is expressed in print coordinates, so it will need to be translated by @@ -3277,7 +3211,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string return gcode; } -bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) +bool GCodeGenerator::needs_retraction(const Polyline &travel, ExtrusionRole role) { if (! m_writer.extruder() || travel.length() < scale_(EXTRUDER_CONFIG(retract_before_travel))) { // skip retraction if the move is shorter than the configured threshold @@ -3316,7 +3250,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) return true; } -std::string GCode::retract(bool toolchange) +std::string GCodeGenerator::retract(bool toolchange) { std::string gcode; @@ -3342,7 +3276,7 @@ std::string GCode::retract(bool toolchange) return gcode; } -std::string GCode::set_extruder(unsigned int extruder_id, double print_z) +std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_z) { if (!m_writer.need_toolchange(extruder_id)) return ""; @@ -3379,7 +3313,12 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) unsigned int old_extruder_id = m_writer.extruder()->id(); const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); if (! end_filament_gcode.empty()) { - gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); + config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(old_extruder_id))); + gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id, &config); check_add_eol(gcode); } } @@ -3444,20 +3383,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) } // convert a model-space scaled point into G-code coordinates -Vec2d GCode::point_to_gcode(const Point &point) const -{ - Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); - return unscaled(point) + m_origin - extruder_offset; -} - -Vec2d GCode::point_to_gcode_quantized(const Point &point) const -{ - Vec2d p = this->point_to_gcode(point); - return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) }; -} - -// convert a model-space scaled point into G-code coordinates -Point GCode::gcode_to_point(const Vec2d &point) const +Point GCodeGenerator::gcode_to_point(const Vec2d &point) const { Vec2d pt = point - m_origin; if (const Extruder *extruder = m_writer.extruder(); extruder) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index dbbfc1e6b9..ae1cc333b1 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -19,18 +19,22 @@ #include "JumpPointSearch.hpp" #include "libslic3r.h" #include "ExPolygon.hpp" -#include "GCodeWriter.hpp" #include "Layer.hpp" #include "Point.hpp" #include "PlaceholderParser.hpp" #include "PrintConfig.hpp" +#include "Geometry/ArcWelder.hpp" #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" #include "GCode/FindReplace.hpp" +#include "GCode/GCodeWriter.hpp" +#include "GCode/PressureEqualizer.hpp" #include "GCode/RetractWhenCrossingPerimeters.hpp" +#include "GCode/SmoothPath.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" -#include "GCode/WipeTower.hpp" +#include "GCode/Wipe.hpp" +#include "GCode/WipeTowerIntegration.hpp" #include "GCode/SeamPlacer.hpp" #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" @@ -40,12 +44,12 @@ #include #include -#include "GCode/PressureEqualizer.hpp" +//#include "GCode/PressureEqualizer.hpp" namespace Slic3r { // Forward declarations. -class GCode; +class GCodeGenerator; namespace { struct Item; } struct PrintInstance; @@ -55,71 +59,11 @@ public: bool enable; OozePrevention() : enable(false) {} - std::string pre_toolchange(GCode &gcodegen); - std::string post_toolchange(GCode &gcodegen); + std::string pre_toolchange(GCodeGenerator &gcodegen); + std::string post_toolchange(GCodeGenerator &gcodegen); private: - int _get_temp(const GCode &gcodegen) const; -}; - -class Wipe { -public: - bool enable; - Polyline path; - - Wipe() : enable(false) {} - bool has_path() const { return ! this->path.empty(); } - void reset_path() { this->path.clear(); } - std::string wipe(GCode &gcodegen, bool toolchange); -}; - -class WipeTowerIntegration { -public: - WipeTowerIntegration( - const PrintConfig &print_config, - const std::vector &priming, - const std::vector> &tool_changes, - const WipeTower::ToolChangeResult &final_purge) : - m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), - m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), - m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), - m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), - m_extruder_offsets(print_config.extruder_offset.values), - m_priming(priming), - m_tool_changes(tool_changes), - m_final_purge(final_purge), - m_layer_idx(-1), - m_tool_change_idx(0) - {} - - std::string prime(GCode &gcodegen); - void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } - std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); - std::string finalize(GCode &gcodegen); - std::vector used_filament_length() const; - -private: - WipeTowerIntegration& operator=(const WipeTowerIntegration&); - std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; - - // Postprocesses gcode: rotates and moves G1 extrusions and returns result - std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; - - // Left / right edges of the wipe tower, for the planning of wipe moves. - const float m_left; - const float m_right; - const Vec2f m_wipe_tower_pos; - const float m_wipe_tower_rotation; - const std::vector m_extruder_offsets; - - // Reference to cached values at the Printer class. - const std::vector &m_priming; - const std::vector> &m_tool_changes; - const WipeTower::ToolChangeResult &m_final_purge; - // Current layer index. - int m_layer_idx; - int m_tool_change_idx; - double m_last_wipe_tower_print_z = 0.f; + int _get_temp(const GCodeGenerator &gcodegen) const; }; class ColorPrintColors @@ -143,9 +87,10 @@ struct LayerResult { static LayerResult make_nop_layer_result() { return {"", std::numeric_limits::max(), false, false, true}; } }; -class GCode { +class GCodeGenerator { + public: - GCode() : + GCodeGenerator() : m_origin(Vec2d::Zero()), m_enable_loop_clipping(true), m_enable_cooling_markers(false), @@ -167,7 +112,7 @@ public: m_silent_time_estimator_enabled(false), m_last_obj_copy(nullptr, Point(std::numeric_limits::max(), std::numeric_limits::max())) {} - ~GCode() = default; + ~GCodeGenerator() = default; // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). @@ -179,9 +124,19 @@ public: void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } const Point& last_pos() const { return m_last_pos; } // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset. - Vec2d point_to_gcode(const Point &point) const; + template + Vec2d point_to_gcode(const Eigen::MatrixBase &point) const { + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "GCodeGenerator::point_to_gcode(): first parameter is not a 2D vector"); + return Vec2d(unscaled(point.x()), unscaled(point.y())) + m_origin + - m_config.extruder_offset.get_at(m_writer.extruder()->id()); + } // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset and quantized to G-code resolution. - Vec2d point_to_gcode_quantized(const Point &point) const; + template + Vec2d point_to_gcode_quantized(const Eigen::MatrixBase &point) const { + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "GCodeGenerator::point_to_gcode_quantized(): first parameter is not a 2D vector"); + Vec2d p = this->point_to_gcode(point); + return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) }; + } Point gcode_to_point(const Vec2d &point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } @@ -201,6 +156,8 @@ public: // append full config to the given string static void append_full_config(const Print& print, std::string& str); + // translate full config into a list of items + static void encode_full_config(const Print& print, std::vector>& config); // Object and support extrusions of the same PrintObject at the same print_z. // public, so that it could be accessed by free helper functions from GCode.cpp @@ -264,6 +221,7 @@ private: // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, + const GCode::SmoothPathCaches &smooth_path_caches, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -278,6 +236,7 @@ private: const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream); // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser @@ -287,6 +246,7 @@ private: const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream); void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } @@ -294,10 +254,13 @@ private: void set_extruders(const std::vector &extruder_ids); std::string preamble(); std::string change_layer(coordf_t print_z); - std::string extrude_entity(const ExtrusionEntity &entity, const std::string_view description, double speed = -1.); - std::string extrude_loop(ExtrusionLoop loop, const std::string_view description, double speed = -1.); - std::string extrude_multi_path(ExtrusionMultiPath multipath, const std::string_view description, double speed = -1.); - std::string extrude_path(ExtrusionPath path, const std::string_view description, double speed = -1.); + std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_skirt(const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, + const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed); + + std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); struct InstanceToPrint { @@ -331,12 +294,14 @@ private: const ObjectLayerToPrint &layer_to_print, // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, + // Optional smooth path interpolating extrusion polylines. + const GCode::SmoothPathCache &smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). const bool print_wipe_extrusions); - std::string extrude_support(const ExtrusionEntityCollection &support_fills); + std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); @@ -347,8 +312,6 @@ private: // Cache for custom seam enforcers/blockers for each layer. SeamPlacer m_seam_placer; - ExtrusionQualityEstimator m_extrusion_quality_estimator; - /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() methods. */ @@ -389,7 +352,7 @@ private: } m_placeholder_parser_integration; OozePrevention m_ooze_prevention; - Wipe m_wipe; + GCode::Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; JPSPathFinder m_avoid_crossing_curled_overhangs; RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters; @@ -432,7 +395,7 @@ private: std::unique_ptr m_spiral_vase; std::unique_ptr m_find_replace; std::unique_ptr m_pressure_equalizer; - std::unique_ptr m_wipe_tower; + std::unique_ptr m_wipe_tower; // Heights (print_z) at which the skirt has already been extruded. std::vector m_skirt_done; @@ -448,7 +411,8 @@ private: // Processor GCodeProcessor m_processor; - std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1); + std::string _extrude( + const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, const std::string_view description, double speed = -1); void print_machine_envelope(GCodeOutputStream &file, Print &print); void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); @@ -457,8 +421,12 @@ private: // To control print speed of 1st object layer over raft interface. bool object_layer_over_raft() const { return m_object_layer_over_raft; } - friend class Wipe; - friend class WipeTowerIntegration; + // Fill in cache of smooth paths for perimeters, fills and supports of the given object layers. + // Based on params, the paths are either decimated to sparser polylines, or interpolated with circular arches. + static void smooth_path_interpolate(const ObjectLayerToPrint &layers, const GCode::SmoothPathCache::InterpolationParameters ¶ms, GCode::SmoothPathCache &out); + + friend class GCode::Wipe; + friend class GCode::WipeTowerIntegration; friend class PressureEqualizer; }; diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index e4dde7a6e2..04d3aa36c6 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -734,7 +734,7 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec return false; } -static bool need_wipe(const GCode &gcodegen, +static bool need_wipe(const GCodeGenerator &gcodegen, const ExPolygons &lslices_offset, const std::vector &lslices_offset_bboxes, const EdgeGrid::Grid &grid_lslices_offset, @@ -1171,7 +1171,7 @@ static void init_boundary(AvoidCrossingPerimeters::Boundary *boundary, Polygons } // Plan travel, which avoids perimeter crossings by following the boundaries of the layer. -Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) +Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, const Point &point, bool *could_be_wipe_disabled) { // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. @@ -1474,7 +1474,7 @@ static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary } // Plan travel, which avoids perimeter crossings by following the boundaries of the layer. -Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) +Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, const Point &point, bool *could_be_wipe_disabled) { // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index ab8d3d6942..932f0b5efb 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -12,7 +12,7 @@ namespace Slic3r { // Forward declarations. -class GCode; +class GCodeGenerator; class Layer; class Point; @@ -29,13 +29,13 @@ public: void init_layer(const Layer &layer); - Polyline travel_to(const GCode& gcodegen, const Point& point) + Polyline travel_to(const GCodeGenerator &gcodegen, const Point& point) { bool could_be_wipe_disabled; return this->travel_to(gcodegen, point, &could_be_wipe_disabled); } - Polyline travel_to(const GCode& gcodegen, const Point& point, bool* could_be_wipe_disabled); + Polyline travel_to(const GCodeGenerator &gcodegen, const Point& point, bool* could_be_wipe_disabled); struct Boundary { // Collection of boundaries used for detection of crossing perimeters for travels diff --git a/src/libslic3r/GCode/ConflictChecker.hpp b/src/libslic3r/GCode/ConflictChecker.hpp index 2e0d9c1810..0fee49ed0d 100644 --- a/src/libslic3r/GCode/ConflictChecker.hpp +++ b/src/libslic3r/GCode/ConflictChecker.hpp @@ -48,7 +48,7 @@ public: void raise() { if (valid()) { - if (_piles[_curPileIdx].empty() == false) { _curHeight += _piles[_curPileIdx].front().height; } + if (_piles[_curPileIdx].empty() == false) { _curHeight += _piles[_curPileIdx].front().height(); } _curPileIdx++; } } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 84ce04b681..33aa46b6d8 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -29,7 +29,7 @@ namespace Slic3r { -CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0) +CoolingBuffer::CoolingBuffer(GCodeGenerator &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0) { this->reset(gcodegen.writer().get_position()); @@ -43,36 +43,45 @@ CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_t void CoolingBuffer::reset(const Vec3d &position) { - m_current_pos.assign(5, 0.f); - m_current_pos[0] = float(position.x()); - m_current_pos[1] = float(position.y()); - m_current_pos[2] = float(position.z()); - m_current_pos[4] = float(m_config.travel_speed.value); + assert(m_current_pos.size() == 5); + m_current_pos[AxisIdx::X] = float(position.x()); + m_current_pos[AxisIdx::Y] = float(position.y()); + m_current_pos[AxisIdx::Z] = float(position.z()); + m_current_pos[AxisIdx::E] = 0.f; + m_current_pos[AxisIdx::F] = float(m_config.travel_speed.value); m_fan_speed = -1; } struct CoolingLine { - enum Type { + enum Type : uint32_t { TYPE_SET_TOOL = 1 << 0, TYPE_EXTRUDE_END = 1 << 1, TYPE_BRIDGE_FAN_START = 1 << 2, TYPE_BRIDGE_FAN_END = 1 << 3, TYPE_G0 = 1 << 4, TYPE_G1 = 1 << 5, - TYPE_ADJUSTABLE = 1 << 6, - TYPE_EXTERNAL_PERIMETER = 1 << 7, + // G2 or G3: Arc interpolation + TYPE_G2G3 = 1 << 6, + TYPE_ADJUSTABLE = 1 << 7, + TYPE_EXTERNAL_PERIMETER = 1 << 8, + // Arc interpolation, counter-clockwise. + TYPE_G2G3_CCW = 1 << 9, + // Arc interpolation, arc defined by IJ (offset of arc center from its start position). + TYPE_G2G3_IJ = 1 << 10, + // Arc interpolation, arc defined by R (arc radius, positive - smaller, negative - larger). + TYPE_G2G3_R = 1 << 11, // The line sets a feedrate. - TYPE_HAS_F = 1 << 8, - TYPE_WIPE = 1 << 9, - TYPE_G4 = 1 << 10, - TYPE_G92 = 1 << 11, + TYPE_HAS_F = 1 << 12, + TYPE_WIPE = 1 << 13, + TYPE_G4 = 1 << 14, + TYPE_G92 = 1 << 15, // Would be TYPE_ADJUSTABLE, but the block of G-code lines has zero extrusion length, thus the block // cannot have its speed adjusted. This should not happen (sic!). - TYPE_ADJUSTABLE_EMPTY = 1 << 12, + TYPE_ADJUSTABLE_EMPTY = 1 << 16, // Custom fan speed (introduced for overhang fan speed) - TYPE_SET_FAN_SPEED = 1 << 13, - TYPE_RESET_FAN_SPEED = 1 << 14, + TYPE_SET_FAN_SPEED = 1 << 17, + TYPE_RESET_FAN_SPEED = 1 << 18, }; CoolingLine(unsigned int type, size_t line_start, size_t line_end) : @@ -334,7 +343,7 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b // Parse the layer G-code for the moves, which could be adjusted. // Return the list of parsed lines, bucketed by an extruder. -std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const +std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::array ¤t_pos) const { std::vector per_extruder_adjustments(m_extruder_ids.size()); std::vector map_extruder_to_per_extruder_adjustment(m_num_extruders, 0); @@ -357,7 +366,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); - std::vector new_pos; + std::array new_pos; for (; *line_start != 0; line_start = line_end) { while (*line_end != '\n' && *line_end != 0) @@ -372,12 +381,20 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_G0; else if (boost::starts_with(sline, "G1 ")) line.type = CoolingLine::TYPE_G1; + else if (boost::starts_with(sline, "G2 ")) + // Arc, clockwise. + line.type = CoolingLine::TYPE_G2G3; + else if (boost::starts_with(sline, "G3 ")) + // Arc, counter-clockwise. + line.type = CoolingLine::TYPE_G2G3 | CoolingLine::TYPE_G2G3_CCW; else if (boost::starts_with(sline, "G92 ")) line.type = CoolingLine::TYPE_G92; if (line.type) { - // G0, G1 or G92 + // G0, G1, G2, G3 or G92 + // Initialize current_pos from new_pos, set IJKR to zero. + std::fill(std::copy(std::begin(current_pos), std::end(current_pos), std::begin(new_pos)), + std::end(new_pos), 0.f); // Parse the G-code line. - new_pos = current_pos; for (auto c = sline.begin() + 3;;) { // Skip whitespaces. for (; c != sline.end() && (*c == ' ' || *c == '\t'); ++ c); @@ -386,21 +403,31 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // Parse the axis. size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') : - (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1); + (*c == extrusion_axis) ? AxisIdx::E : (*c == 'F') ? AxisIdx::F : + (*c >= 'I' && *c <= 'K') ? int(AxisIdx::I) + (*c - 'I') : + (*c == 'R') ? AxisIdx::R : size_t(-1); if (axis != size_t(-1)) { //auto [pend, ec] = fast_float::from_chars(&*(++ c), sline.data() + sline.size(), new_pos[axis]); - if (axis == 4) { + if (axis == AxisIdx::F) { // Convert mm/min to mm/sec. - new_pos[4] /= 60.f; + new_pos[AxisIdx::F] /= 60.f; if ((line.type & CoolingLine::TYPE_G92) == 0) // This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls. line.type |= CoolingLine::TYPE_HAS_F; - } + } else if (axis >= AxisIdx::I && axis <= AxisIdx::J) + line.type |= CoolingLine::TYPE_G2G3_IJ; + else if (axis == AxisIdx::R) + line.type |= CoolingLine::TYPE_G2G3_R; } // Skip this word. for (; c != sline.end() && *c != ' ' && *c != '\t'; ++ c); } + // If G2 or G3, then either center of the arc or radius has to be defined. + assert(! (line.type & CoolingLine::TYPE_G2G3) || + (line.type & (CoolingLine::TYPE_G2G3_IJ | CoolingLine::TYPE_G2G3_R))); + // Arc is defined either by IJ or by R, not by both. + assert(! ((line.type & CoolingLine::TYPE_G2G3_IJ) && (line.type & CoolingLine::TYPE_G2G3_R))); bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER"); bool wipe = boost::contains(sline, ";_WIPE"); if (external_perimeter) @@ -412,23 +439,41 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: active_speed_modifier = adjustment->lines.size(); } if ((line.type & CoolingLine::TYPE_G92) == 0) { - // G0 or G1. Calculate the duration. + // G0, G1, G2, G3. Calculate the duration. + assert((line.type & CoolingLine::TYPE_G0) != 0 + (line.type & CoolingLine::TYPE_G1) != 0 + (line.type & CoolingLine::TYPE_G2G3) != 0 == 1); if (m_config.use_relative_e_distances.value) // Reset extruder accumulator. - current_pos[3] = 0.f; + current_pos[AxisIdx::E] = 0.f; float dif[4]; for (size_t i = 0; i < 4; ++ i) dif[i] = new_pos[i] - current_pos[i]; - float dxy2 = dif[0] * dif[0] + dif[1] * dif[1]; - float dxyz2 = dxy2 + dif[2] * dif[2]; + float dxy2; + if (line.type & CoolingLine::TYPE_G2G3) { + // Measure arc length. + if (line.type & CoolingLine::TYPE_G2G3_IJ) { + dxy2 = sqr(Geometry::ArcWelder::arc_length( + Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), + Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), + Vec2d(current_pos[AxisIdx::X] + new_pos[AxisIdx::I], current_pos[AxisIdx::Y] + new_pos[AxisIdx::J]), + line.type & CoolingLine::TYPE_G2G3_CCW)); + } else if (line.type & CoolingLine::TYPE_G2G3_R) { + dxy2 = sqr(Geometry::ArcWelder::arc_length( + Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), + Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), + double(new_pos[AxisIdx::R]))); + } else + dxy2 = 0; + } else + dxy2 = sqr(dif[AxisIdx::X]) + sqr(dif[AxisIdx::Y]); + float dxyz2 = dxy2 + sqr(dif[AxisIdx::Z]); if (dxyz2 > 0.f) { // Movement in xyz, calculate time from the xyz Euclidian distance. line.length = sqrt(dxyz2); - } else if (std::abs(dif[3]) > 0.f) { + } else if (std::abs(dif[AxisIdx::E]) > 0.f) { // Movement in the extruder axis. - line.length = std::abs(dif[3]); + line.length = std::abs(dif[AxisIdx::E]); } - line.feedrate = new_pos[4]; + line.feedrate = new_pos[AxisIdx::F]; assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f); if (line.length > 0) { assert(line.feedrate > 0); @@ -440,7 +485,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: assert(adjustment->min_print_speed >= 0); line.time_max = (adjustment->min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->min_print_speed); } - if (active_speed_modifier < adjustment->lines.size() && (line.type & CoolingLine::TYPE_G1)) { + if (active_speed_modifier < adjustment->lines.size() && (line.type & (CoolingLine::TYPE_G1 | CoolingLine::TYPE_G2G3))) { // Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry. assert((line.type & CoolingLine::TYPE_HAS_F) == 0); CoolingLine &sm = adjustment->lines[active_speed_modifier]; @@ -457,7 +502,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = 0; } } - current_pos = std::move(new_pos); + std::copy(std::begin(new_pos), std::begin(new_pos) + 5, std::begin(current_pos)); } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { // Closing a block of non-zero length extrusion moves. line.type = CoolingLine::TYPE_EXTRUDE_END; diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 6451d32011..ff4da44da1 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -17,7 +17,7 @@ namespace Slic3r { -class GCode; +class GCodeGenerator; class Layer; struct PerExtruderAdjustments; @@ -32,7 +32,7 @@ struct PerExtruderAdjustments; // class CoolingBuffer { public: - CoolingBuffer(GCode &gcodegen); + CoolingBuffer(GCodeGenerator &gcodegen); void reset(const Vec3d &position); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } std::string process_layer(std::string &&gcode, size_t layer_id, bool flush); @@ -41,7 +41,7 @@ public: private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; - std::vector parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const; + std::vector parse_layer_gcode(const std::string &gcode, std::array ¤t_pos) const; float calculate_layer_slowdown(std::vector &per_extruder_adjustments); // Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed. // Returns the adjusted G-code. @@ -50,9 +50,11 @@ private: // G-code snippet cached for the support layers preceding an object layer. std::string m_gcode; // Internal data. - // X,Y,Z,E,F std::vector m_axis; - std::vector m_current_pos; + enum AxisIdx : int { + X = 0, Y, Z, E, F, I, J, K, R, Count + }; + std::array m_current_pos; // Current known fan speed or -1 if not known yet. int m_fan_speed; // Cached from GCodeWriter. @@ -61,7 +63,7 @@ private: // Highest of m_extruder_ids plus 1. unsigned int m_num_extruders { 0 }; const std::string m_toolchange_prefix; - // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, + // Referencs GCodeGenerator::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. const PrintConfig &m_config; unsigned int m_current_extruder; diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp new file mode 100644 index 0000000000..8ebc5d3df1 --- /dev/null +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -0,0 +1,216 @@ +#include "ExtrusionProcessor.hpp" +#include + +namespace Slic3r { namespace ExtrusionProcessor { + +ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) +{ + std::vector extended_points = estimate_points_properties(path.polyline.points, + unscaled_prev_layer, path.width()); + std::vector> calculated_distances(extended_points.size()); + + for (size_t i = 0; i < extended_points.size(); i++) { + const ExtendedPoint &curr = extended_points[i]; + const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; + + // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines + float proximity_to_curled_lines = 0.0; + const double dist_limit = 10.0 * path.width(); + { + Vec2d middle = 0.5 * (curr.position + next.position); + auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); + if (!line_indices.empty()) { + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle + // of this long line + // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. + // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point + // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge + if (len > 8) { + Vec2d dir = Vec2d(next.position - curr.position) / len; + Vec2d right = Vec2d(-dir.y(), dir.x()); + + Polygon box_of_influence = { + scaled(Vec2d(curr.position + right * dist_limit)), + scaled(Vec2d(next.position + right * dist_limit)), + scaled(Vec2d(next.position - right * dist_limit)), + scaled(Vec2d(curr.position - right * dist_limit)), + }; + + double projected_lengths_sum = 0; + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); + if (inside.empty()) + continue; + double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); + projected_lengths_sum += projected_length; + } + if (projected_lengths_sum < 0.4 * len) { + line_indices.clear(); + } + } + + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); + float proximity = (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * + (line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + proximity_to_curled_lines = std::max(proximity_to_curled_lines, proximity); + } + } + } + calculated_distances[i].first = std::max(curr.distance, next.distance); + calculated_distances[i].second = proximity_to_curled_lines; + } + + ExtrusionPaths result; + ExtrusionAttributes new_attrs = path.attributes(); + new_attrs.overhang_attributes = std::optional( + {calculated_distances[0].first, calculated_distances[0].first, calculated_distances[0].second}); + result.emplace_back(new_attrs); + result.back().polyline.append(Point::new_scale(extended_points[0].position)); + size_t sequence_start_index = 0; + for (size_t i = 1; i < extended_points.size(); i++) { + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + result.back().overhang_attributes_mutable()->end_distance_from_prev_layer = extended_points[i].distance; + + if (std::abs(calculated_distances[sequence_start_index].first - calculated_distances[i].first) < 0.001 * path.attributes().width && + std::abs(calculated_distances[sequence_start_index].second - calculated_distances[i].second) < 0.001) { + // do not start new path, the attributes are similar enough + // NOTE: a larger tolerance may be applied here. However, it makes the gcode preview much less smooth + // (But it has very likely zero impact on the print quality.) + } else if (i + 1 < extended_points.size()) { // do not start new path if this is last point! + // start new path, parameters differ + new_attrs.overhang_attributes->start_distance_from_prev_layer = calculated_distances[i].first; + new_attrs.overhang_attributes->end_distance_from_prev_layer = calculated_distances[i].first; + new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second; + sequence_start_index = i; + result.emplace_back(new_attrs); + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + } + } + + return result; +}; + +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection *ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) +{ + ExtrusionEntityCollection result{}; + result.no_sort = ecc->no_sort; + for (const auto *e : ecc->entities) { + if (auto *col = dynamic_cast(e)) { + result.append(calculate_and_split_overhanging_extrusions(col, unscaled_prev_layer, prev_layer_curled_lines)); + } else if (auto *loop = dynamic_cast(e)) { + ExtrusionLoop new_loop = *loop; + new_loop.paths.clear(); + for (const ExtrusionPath &p : loop->paths) { + auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines); + new_loop.paths.insert(new_loop.paths.end(), paths.begin(), paths.end()); + } + result.append(new_loop); + } else if (auto *mp = dynamic_cast(e)) { + ExtrusionMultiPath new_mp = *mp; + new_mp.paths.clear(); + for (const ExtrusionPath &p : mp->paths) { + auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines); + new_mp.paths.insert(new_mp.paths.end(), paths.begin(), paths.end()); + } + result.append(new_mp); + } else if (auto *op = dynamic_cast(e)) { + auto paths = calculate_and_split_overhanging_extrusions(*op, unscaled_prev_layer, prev_layer_curled_lines); + for (const ExtrusionPath &p : paths) { + result.append(ExtrusionPathOriented(p.polyline, p.attributes())); + } + } else if (auto *p = dynamic_cast(e)) { + auto paths = calculate_and_split_overhanging_extrusions(*p, unscaled_prev_layer, prev_layer_curled_lines); + result.append(paths); + } else { + throw Slic3r::InvalidArgument("Unknown extrusion entity type"); + } + } + return result; +}; + + +std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + size_t extruder_id, + float external_perim_reference_speed, + float default_speed) +{ + assert(attributes.overhang_attributes.has_value()); + std::vector> overhangs_with_speeds = { + {100, ConfigOptionFloatOrPercent{default_speed, false}}}; + if (config.enable_dynamic_overhang_speeds) { + overhangs_with_speeds = {{0, config.overhang_speed_0}, + {25, config.overhang_speed_1}, + {50, config.overhang_speed_2}, + {75, config.overhang_speed_3}, + {100, ConfigOptionFloatOrPercent{default_speed, false}}}; + } + + std::vector> overhang_with_fan_speeds = {{100, ConfigOptionInts{0}}}; + if (config.enable_dynamic_fan_speeds.get_at(extruder_id)) { + overhang_with_fan_speeds = {{0, config.overhang_fan_speed_0}, + {25, config.overhang_fan_speed_1}, + {50, config.overhang_fan_speed_2}, + {75, config.overhang_fan_speed_3}, + {100, ConfigOptionInts{0}}}; + } + + float speed_base = external_perim_reference_speed > 0 ? external_perim_reference_speed : default_speed; + std::map speed_sections; + for (size_t i = 0; i < overhangs_with_speeds.size(); i++) { + float distance = attributes.width * (1.0 - (overhangs_with_speeds[i].first / 100.0)); + float speed = overhangs_with_speeds[i].second.percent ? (speed_base * overhangs_with_speeds[i].second.value / 100.0) : + overhangs_with_speeds[i].second.value; + if (speed < EPSILON) + speed = speed_base; + speed_sections[distance] = speed; + } + + std::map fan_speed_sections; + for (size_t i = 0; i < overhang_with_fan_speeds.size(); i++) { + float distance = attributes.width * (1.0 - (overhang_with_fan_speeds[i].first / 100.0)); + float fan_speed = overhang_with_fan_speeds[i].second.get_at(extruder_id); + fan_speed_sections[distance] = fan_speed; + } + + auto interpolate_speed = [](const std::map &values, float distance) { + auto upper_dist = values.lower_bound(distance); + if (upper_dist == values.end()) { + return values.rbegin()->second; + } + if (upper_dist == values.begin()) { + return upper_dist->second; + } + + auto lower_dist = std::prev(upper_dist); + float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); + return (1.0f - t) * lower_dist->second + t * upper_dist->second; + }; + + float extrusion_speed = std::min(interpolate_speed(speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), + interpolate_speed(speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + float curled_base_speed = interpolate_speed(speed_sections, + attributes.width * attributes.overhang_attributes->proximity_to_curled_lines); + float final_speed = std::min(curled_base_speed, extrusion_speed); + float fan_speed = std::min(interpolate_speed(fan_speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), + interpolate_speed(fan_speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + + if (!config.enable_dynamic_overhang_speeds) { + final_speed = -1; + } + if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) { + fan_speed = -1; + } + + return {final_speed, fan_speed}; +} + +}} // namespace Slic3r::ExtrusionProcessor \ No newline at end of file diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index f3256a54ef..3e2bbf080d 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -18,25 +18,29 @@ #include "../Flow.hpp" #include "../Config.hpp" #include "../Line.hpp" +#include "../Exception.hpp" +#include "../PrintConfig.hpp" #include +#include #include #include #include #include #include +#include #include #include #include #include -namespace Slic3r { +namespace Slic3r { namespace ExtrusionProcessor { struct ExtendedPoint { - Vec2d position; - float distance; - float curvature; + Vec2d position; + float distance; + float curvature; }; template @@ -50,27 +54,30 @@ std::vector estimate_points_properties(const POINTS using AABBScalar = typename AABBTreeLines::LinesDistancer::Scalar; if (input_points.empty()) return {}; - float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; - auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; + float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; + auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; std::vector points; points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); { ExtendedPoint start_point{maybe_unscale(input_points.front())}; - auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); - start_point.distance = distance + boundary_offset; + auto [distance, nearest_line, + x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); + start_point.distance = distance + boundary_offset; points.push_back(start_point); } for (size_t i = 1; i < input_points.size(); i++) { ExtendedPoint next_point{maybe_unscale(input_points[i])}; - auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); - next_point.distance = distance + boundary_offset; + auto [distance, nearest_line, + x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); + next_point.distance = distance + boundary_offset; if (ADD_INTERSECTIONS && ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { - const ExtendedPoint &prev_point = points.back(); - auto intersections = unscaled_prev_layer.template intersections_with_line(L{prev_point.position.cast(), next_point.position.cast()}); + const ExtendedPoint &prev_point = points.back(); + auto intersections = unscaled_prev_layer.template intersections_with_line( + L{prev_point.position.cast(), next_point.position.cast()}); for (const auto &intersection : intersections) { ExtendedPoint p{}; p.position = intersection.first.template cast(); @@ -83,47 +90,49 @@ std::vector estimate_points_properties(const POINTS if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) { std::vector new_points; - new_points.reserve(points.size()*2); + new_points.reserve(points.size() * 2); new_points.push_back(points.front()); for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) { const ExtendedPoint &curr = points[point_idx]; const ExtendedPoint &next = points[point_idx + 1]; - if ((curr.distance > 0 && curr.distance < boundary_offset + 2.0f) || - (next.distance > 0 && next.distance < boundary_offset + 2.0f)) { + if ((curr.distance > -boundary_offset && curr.distance < boundary_offset + 2.0f) || + (next.distance > -boundary_offset && next.distance < boundary_offset + 2.0f)) { double line_len = (next.position - curr.position).norm(); if (line_len > 4.0f) { - double a0 = std::clamp((curr.distance + 2 * boundary_offset) / line_len, 0.0, 1.0); - double a1 = std::clamp(1.0f - (next.distance + 2 * boundary_offset) / line_len, 0.0, 1.0); + double a0 = std::clamp((curr.distance + 3 * boundary_offset) / line_len, 0.0, 1.0); + double a1 = std::clamp(1.0f - (next.distance + 3 * boundary_offset) / line_len, 0.0, 1.0); double t0 = std::min(a0, a1); double t1 = std::max(a0, a1); if (t0 < 1.0) { - auto p0 = curr.position + t0 * (next.position - curr.position); - auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); + auto p0 = curr.position + t0 * (next.position - curr.position); + auto [p0_dist, p0_near_l, + p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); ExtendedPoint new_p{}; - new_p.position = p0; - new_p.distance = float(p0_dist + boundary_offset); + new_p.position = p0; + new_p.distance = float(p0_dist + boundary_offset); new_points.push_back(new_p); } if (t1 > 0.0) { - auto p1 = curr.position + t1 * (next.position - curr.position); - auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); + auto p1 = curr.position + t1 * (next.position - curr.position); + auto [p1_dist, p1_near_l, + p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); ExtendedPoint new_p{}; - new_p.position = p1; - new_p.distance = float(p1_dist + boundary_offset); + new_p.position = p1; + new_p.distance = float(p1_dist + boundary_offset); new_points.push_back(new_p); } } } new_points.push_back(next); } - points = new_points; + points = std::move(new_points); } if (max_line_length > 0) { std::vector new_points; - new_points.reserve(points.size()*2); + new_points.reserve(points.size() * 2); { for (size_t i = 0; i + 1 < points.size(); i++) { const ExtendedPoint &curr = points[i]; @@ -137,14 +146,14 @@ std::vector estimate_points_properties(const POINTS auto [p_dist, p_near_l, p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.cast()); ExtendedPoint new_p{}; - new_p.position = pos; - new_p.distance = float(p_dist + boundary_offset); + new_p.position = pos; + new_p.distance = float(p_dist + boundary_offset); new_points.push_back(new_p); } } new_points.push_back(points.back()); } - points = new_points; + points = std::move(new_points); } std::vector angles_for_curvature(points.size()); @@ -216,144 +225,21 @@ std::vector estimate_points_properties(const POINTS return points; } -struct ProcessedPoint -{ - Point p; - float speed = 1.0f; - int fan_speed = 0; -}; +ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); -class ExtrusionQualityEstimator -{ - std::unordered_map> prev_layer_boundaries; - std::unordered_map> next_layer_boundaries; - std::unordered_map> prev_curled_extrusions; - std::unordered_map> next_curled_extrusions; - const PrintObject *current_object; +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions( + const ExtrusionEntityCollection *ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); -public: - void set_current_object(const PrintObject *object) { current_object = object; } +std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + size_t extruder_id, + float external_perim_reference_speed, + float default_speed); - void prepare_for_new_layer(const Layer *layer) - { - if (layer == nullptr) - return; - const PrintObject *object = layer->object(); - prev_layer_boundaries[object] = next_layer_boundaries[object]; - next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; - prev_curled_extrusions[object] = next_curled_extrusions[object]; - next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{layer->curled_lines}; - } - - std::vector estimate_speed_from_extrusion_quality( - const ExtrusionPath &path, - const std::vector> overhangs_w_speeds, - const std::vector> overhangs_w_fan_speeds, - size_t extruder_id, - float ext_perimeter_speed, - float original_speed) - { - float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; - std::map speed_sections; - for (size_t i = 0; i < overhangs_w_speeds.size(); i++) { - float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); - float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : - overhangs_w_speeds[i].second.value; - if (speed < EPSILON) speed = speed_base; - speed_sections[distance] = speed; - } - - std::map fan_speed_sections; - for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) { - float distance = path.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); - float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); - fan_speed_sections[distance] = fan_speed; - } - - std::vector extended_points = - estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); - - std::vector processed_points; - processed_points.reserve(extended_points.size()); - for (size_t i = 0; i < extended_points.size(); i++) { - const ExtendedPoint &curr = extended_points[i]; - const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; - - // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines - float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * path.width; - { - Vec2d middle = 0.5 * (curr.position + next.position); - auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); - if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line - // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. - // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point - // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge - if (len > 8) { - Vec2d dir = Vec2d(next.position - curr.position) / len; - Vec2d right = Vec2d(-dir.y(), dir.x()); - - Polygon box_of_influence = { - scaled(Vec2d(curr.position + right * dist_limit)), - scaled(Vec2d(next.position + right * dist_limit)), - scaled(Vec2d(next.position - right * dist_limit)), - scaled(Vec2d(curr.position - right * dist_limit)), - }; - - double projected_lengths_sum = 0; - for (size_t idx : line_indices) { - const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); - Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); - if (inside.empty()) - continue; - double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); - projected_lengths_sum += projected_length; - } - if (projected_lengths_sum < 0.4 * len) { - line_indices.clear(); - } - } - - for (size_t idx : line_indices) { - const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); - float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); - float dist = path.width * (1.0 - (distance_from_curled / dist_limit)) * - (1.0 - (distance_from_curled / dist_limit)) * - (line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator - artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); - } - } - } - - auto interpolate_speed = [](const std::map &values, float distance) { - auto upper_dist = values.lower_bound(distance); - if (upper_dist == values.end()) { - return values.rbegin()->second; - } - if (upper_dist == values.begin()) { - return upper_dist->second; - } - - auto lower_dist = std::prev(upper_dist); - float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); - return (1.0f - t) * lower_dist->second + t * upper_dist->second; - }; - - float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), - interpolate_speed(speed_sections, next.distance)); - float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines); - float final_speed = std::min(curled_base_speed, extrusion_speed); - float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), - interpolate_speed(fan_speed_sections, next.distance)); - - processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)}); - } - return processed_points; - } -}; - -} // namespace Slic3r +}} // namespace Slic3r::ExtrusionProcessor #endif // slic3r_ExtrusionProcessor_hpp_ diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 0a77387f69..6e7e8b8944 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -9,8 +9,9 @@ #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/format.hpp" #include "libslic3r/I18N.hpp" -#include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/GCode/GCodeWriter.hpp" #include "libslic3r/I18N.hpp" +#include "libslic3r/Geometry/ArcWelder.hpp" #include "GCodeProcessor.hpp" #include @@ -48,8 +49,6 @@ static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero(); // taken from PrusaResearch.ini - [printer:Original Prusa i3 MK2.5 MMU2] static const std::vector DEFAULT_EXTRUDER_COLORS = { "#FF8000", "#DB5182", "#3EC0FF", "#FF4F4F", "#FBEB7D" }; -static const std::string INTERNAL_G2G3_TAG = "!#!#! internal only - from G2/G3 expansion !#!#!"; - namespace Slic3r { const std::vector GCodeProcessor::Reserved_Tags = { @@ -70,6 +69,19 @@ const std::vector GCodeProcessor::Reserved_Tags = { const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; +bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{ + { + bgcode::core::ECompressionType::None, // file metadata + bgcode::core::ECompressionType::None, // printer metadata + bgcode::core::ECompressionType::Deflate, // print metadata + bgcode::core::ECompressionType::Deflate, // slicer metadata + bgcode::core::ECompressionType::Heatshrink_12_4, // gcode + }, + bgcode::core::EGCodeEncodingType::MeatPackComments, + bgcode::core::EMetadataEncodingType::INI, + bgcode::core::EChecksumType::CRC32 +}; + #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -460,7 +472,7 @@ void GCodeProcessorResult::reset() { } #else void GCodeProcessorResult::reset() { - + is_binary_file = false; moves.clear(); lines_ends.clear(); bed_shape = Pointfs(); @@ -559,6 +571,9 @@ void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); + m_binarizer.set_enabled(config.gcode_binary); + m_result.is_binary_file = config.gcode_binary; + m_producer = EProducer::PrusaSlicer; m_flavor = config.gcode_flavor; @@ -1028,6 +1043,23 @@ static inline const char* remove_eols(const char *begin, const char *end) { // Load a G-code into a stand-alone G-code viewer. // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) +{ + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); + + using namespace bgcode::core; + std::vector cs_buffer(65536); + const bool is_binary = is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success; + fclose(file); + + if (is_binary) + process_binary_file(filename, cancel_callback); + else + process_ascii_file(filename, cancel_callback); +} + +void GCodeProcessor::process_ascii_file(const std::string& filename, std::function cancel_callback) { CNumericLocalesSetter locales_setter; @@ -1078,6 +1110,7 @@ void GCodeProcessor::process_file(const std::string& filename, std::functionfinalize(false); } +static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +{ + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1); + } + if (out_file_pos != nullptr) + *out_file_pos += out_string.size(); +} + +void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) +{ +#if ENABLE_GCODE_VIEWER_STATISTICS + m_start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (file.f == nullptr) + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); + + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, + std::string(translate_result(res)))); + + // read file metadata block, if present + BlockHeader block_header; + std::vector cs_buffer(65536); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::FileMetadata && + (EBlockType)block_header.type != EBlockType::PrinterMetadata) + throw Slic3r::RuntimeError(format("Unable to find file metadata block in file %1%", filename)); + if ((EBlockType)block_header.type == EBlockType::FileMetadata) { + FileMetadataBlock file_metadata_block; + res = file_metadata_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), + [](const std::pair& item) { return item.first == "Producer"; }); + if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) + m_producer = EProducer::PrusaSlicer; + else + m_producer = EProducer::Unknown; + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + } + else { + m_producer = EProducer::Unknown; + } + + // read printer metadata block + if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) + throw Slic3r::RuntimeError(format("Unable to find printer metadata block in file %1%", filename)); + PrinterMetadataBlock printer_metadata_block; + res = printer_metadata_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + + // read thumbnail blocks + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + + while ((EBlockType)block_header.type == EBlockType::Thumbnail) { + ThumbnailBlock thumbnail_block; + res = thumbnail_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + } + + // read print metadata block + if ((EBlockType)block_header.type != EBlockType::PrintMetadata) + throw Slic3r::RuntimeError(format("Unable to find print metadata block in file %1%", filename)); + PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + + // read slicer metadata block + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) + throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file %1%", filename)); + SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + std::string str; + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + str += key + " = " + value + "\n"; + } + // Silently substitute unknown values by new ones for loading configurations from PrusaSlicer's own G-code. + // Showing substitution log or errors may make sense, but we are not really reading many values from the G-code config, + // thus a probability of incorrect substitution is low and the G-code viewer is a consumer-only anyways. + config.load_from_ini_string(str, ForwardCompatibilitySubstitutionRule::EnableSilent); + apply_config(config); + + m_result.filename = filename; + m_result.is_binary_file = true; + m_result.id = ++s_result_id; + initialize_result_moves(); + + // read gcodes block + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::GCode) + throw Slic3r::RuntimeError(format("Unable to find gcode block in file %1%", filename)); + while ((EBlockType)block_header.type == EBlockType::GCode) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + update_lines_ends_and_out_file_pos(block.raw_data, lines_ends, nullptr); + + m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + this->process_gcode_line(line, true); + }); + + if (ftell(file.f) == file_size) + break; + + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + } + + // Don't post-process the G-code to update time stamps. + this->finalize(false); +} + void GCodeProcessor::initialize(const std::string& filename) { assert(is_decimal_separator_point()); @@ -1137,7 +1320,7 @@ void GCodeProcessor::finalize(bool perform_post_process) m_used_filaments.process_caches(this); - update_estimated_times_stats(); + update_estimated_statistics(); #if ENABLE_GCODE_VIEWER_DATA_CHECKING std::cout << "\n"; @@ -2368,13 +2551,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (line.has_e()) g1_axes[E] = (double)line.e(); std::optional g1_feedrate = std::nullopt; if (line.has_f()) g1_feedrate = (double)line.f(); - std::optional g1_cmt = std::nullopt; - if (!line.comment().empty()) g1_cmt = line.comment(); - - process_G1(g1_axes, g1_feedrate, g1_cmt); + process_G1(g1_axes, g1_feedrate); } -void GCodeProcessor::process_G1(const std::array, 4>& axes, std::optional feedrate, std::optional cmt) +void GCodeProcessor::process_G1(const std::array, 4>& axes, std::optional feedrate, G1DiscretizationOrigin origin) { const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); const float filament_radius = 0.5f * filament_diameter; @@ -2457,7 +2637,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes m_height = m_forced_height; else if (m_layer_id == 0) m_height = m_first_layer_height + m_z_offset; - else if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) { + else if (origin == G1DiscretizationOrigin::G1) { if (m_end_position[Z] > m_extruded_last_z + EPSILON && delta_pos[Z] == 0.0) m_height = m_end_position[Z] - m_extruded_last_z; } @@ -2468,7 +2648,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes if (m_end_position[Z] == 0.0f || (m_extrusion_role == GCodeExtrusionRole::Custom && m_layer_id == 0)) m_end_position[Z] = m_height; - if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) + if (origin == G1DiscretizationOrigin::G1) m_extruded_last_z = m_end_position[Z]; m_options_z_corrector.update(m_height); @@ -2698,18 +2878,48 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes } // store move - store_move_vertex(type, cmt.has_value() && *cmt == INTERNAL_G2G3_TAG); + store_move_vertex(type, origin == G1DiscretizationOrigin::G2G3); } void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise) { - if (!line.has('I') || !line.has('J')) + enum class EFitting { None, IJ, R }; + const EFitting fitting = line.has('R') ? EFitting::R : (line.has('I') && line.has('J')) ? EFitting::IJ : EFitting::None; + + if (fitting == EFitting::None) return; + const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); + const float filament_radius = 0.5f * filament_diameter; + const float area_filament_cross_section = static_cast(M_PI) * sqr(filament_radius); + + AxisCoords end_position = m_start_position; + for (unsigned char a = X; a <= E; ++a) { + end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section)); + } + // relative center Vec3f rel_center = Vec3f::Zero(); - if (!line.has_value('I', rel_center.x()) || !line.has_value('J', rel_center.y())) - return; +#ifndef NDEBUG + double radius = 0.0; +#endif // NDEBUG + if (fitting == EFitting::R) { + float r; + if (!line.has_value('R', r) || r == 0.0f) + return; +#ifndef NDEBUG + radius = (double)std::abs(r); +#endif // NDEBUG + const Vec2f start_pos((float)m_start_position[X], (float)m_start_position[Y]); + const Vec2f end_pos((float)end_position[X], (float)end_position[Y]); + const Vec2f c = Geometry::ArcWelder::arc_center(start_pos, end_pos, r, !clockwise); + rel_center.x() = c.x() - m_start_position[X]; + rel_center.y() = c.y() - m_start_position[Y]; + } + else { + if (!line.has_value('I', rel_center.x()) || !line.has_value('J', rel_center.y())) + return; + } // scale center, if needed if (m_units == EUnits::Inches) @@ -2745,15 +2955,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // arc center arc.center = arc.start + rel_center.cast(); - const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); - const float filament_radius = 0.5f * filament_diameter; - const float area_filament_cross_section = static_cast(M_PI) * sqr(filament_radius); - - AxisCoords end_position = m_start_position; - for (unsigned char a = X; a <= E; ++a) { - end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section)); - } - // arc end endpoint arc.end = Vec3d(end_position[X], end_position[Y], end_position[Z]); @@ -2762,6 +2963,8 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // what to do ??? } + assert(fitting != EFitting::R || std::abs(radius - arc.start_radius()) < EPSILON); + // updates feedrate from line std::optional feedrate; if (line.has_f()) @@ -2821,9 +3024,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc g1_feedrate = (double)*feedrate; if (extrusion.has_value()) g1_axes[E] = target[E]; - std::optional g1_cmt = INTERNAL_G2G3_TAG; - - process_G1(g1_axes, g1_feedrate, g1_cmt); + process_G1(g1_axes, g1_feedrate, G1DiscretizationOrigin::G2G3); }; // calculate arc segments @@ -2832,8 +3033,13 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // https://github.com/prusa3d/Prusa-Firmware/blob/MK3/Firmware/motion_control.cpp // segments count +#if 0 static const double MM_PER_ARC_SEGMENT = 1.0; const size_t segments = std::max(std::floor(travel_length / MM_PER_ARC_SEGMENT), 1); +#else + static const double gcode_arc_tolerance = 0.0125; + const size_t segments = Geometry::ArcWelder::arc_discretization_steps(arc.start_radius(), std::abs(arc.angle), gcode_arc_tolerance); +#endif const double inv_segment = 1.0 / double(segments); const double theta_per_segment = arc.angle * inv_segment; @@ -3440,8 +3646,68 @@ void GCodeProcessor::post_process() // temporary file to contain modified gcode std::string out_path = m_result.filename + ".postprocess"; FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") }; - if (out.f == nullptr) { + if (out.f == nullptr) throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); + + std::vector filament_mm(m_result.extruders_count, 0.0); + std::vector filament_cm3(m_result.extruders_count, 0.0); + std::vector filament_g(m_result.extruders_count, 0.0); + std::vector filament_cost(m_result.extruders_count, 0.0); + + double filament_total_g = 0.0; + double filament_total_cost = 0.0; + + for (const auto& [id, volume] : m_result.print_statistics.volumes_per_extruder) { + filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); + filament_cm3[id] = volume * 0.001; + filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); + filament_cost[id] = filament_g[id] * double(m_result.filament_cost[id]) * 0.001; + filament_total_g += filament_g[id]; + filament_total_cost += filament_cost[id]; + } + + if (m_binarizer.is_enabled()) { + // update print metadata + auto stringify = [](const std::vector& values) { + std::string ret; + char buf[1024]; + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%.2lf, " : "%.2lf", values[i]); + ret += buf; + } + return ret; + }; + + // update binary data + bgcode::binarize::BinaryData& binary_data = m_binarizer.get_binary_data(); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedMm, stringify(filament_mm)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedCm3, stringify(filament_cm3)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedG, stringify(filament_g)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentCost, stringify(filament_cost)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g })); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost })); + + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedMm, stringify(filament_mm)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedG, stringify(filament_g)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentCost, stringify(filament_cost)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedCm3, stringify(filament_cm3)); // duplicated into print metadata + + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); + binary_data.print_metadata.raw_data.emplace_back("estimated printing time " + std::string(buf), get_time_dhms(machine.time)); + binary_data.print_metadata.raw_data.emplace_back("estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front())); + + binary_data.printer_metadata.raw_data.emplace_back("estimated printing time " + std::string(buf), get_time_dhms(machine.time)); + } + } + + const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Unable to initialize the gcode binarizer.\nError: %1%", bgcode::core::translate_result(res))); } auto time_in_minutes = [](float time_in_seconds) { @@ -3560,12 +3826,14 @@ void GCodeProcessor::post_process() size_t m_curr_g1_id{ 0 }; size_t m_out_file_pos{ 0 }; + bgcode::binarize::Binarizer& m_binarizer; + public: - ExportLines(EWriteType type, TimeMachine& machine) + ExportLines(bgcode::binarize::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG - : m_statistics(*this), m_write_type(type), m_machine(machine) {} + : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else - : m_write_type(type), m_machine(machine) {} + : m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #endif // NDEBUG void update(size_t lines_counter, size_t g1_lines_counter) { @@ -3678,7 +3946,14 @@ void GCodeProcessor::post_process() } } - write_to_file(out, out_string, result, out_path); + if (m_binarizer.is_enabled()) { + if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while sending gcode to the binarizer."); + } + else { + write_to_file(out, out_string, result, out_path); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + } } // flush the current content of the cache to file @@ -3694,7 +3969,14 @@ void GCodeProcessor::post_process() m_statistics.remove_all_lines(); #endif // NDEBUG - write_to_file(out, out_string, result, out_path); + if (m_binarizer.is_enabled()) { + if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while sending gcode to the binarizer."); + } + else { + write_to_file(out, out_string, result, out_path); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + } } void synchronize_moves(GCodeProcessorResult& result) const { @@ -3713,22 +3995,19 @@ void GCodeProcessor::post_process() private: void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path) { if (!out_string.empty()) { - fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); - if (ferror(out.f)) { - out.close(); - boost::nowide::remove(out_path.c_str()); - throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); + if (!m_binarizer.is_enabled()) { + fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); + if (ferror(out.f)) { + out.close(); + boost::nowide::remove(out_path.c_str()); + throw Slic3r::RuntimeError("GCode processor post process export failed.\nIs the disk full?"); + } } - for (size_t i = 0; i < out_string.size(); ++i) { - if (out_string[i] == '\n') - result.lines_ends.emplace_back(m_out_file_pos + i + 1); - } - m_out_file_pos += out_string.size(); } } }; - ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); + ExportLines export_lines(m_binarizer, m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); // replace placeholder lines with the proper final value // gcode_line is in/out parameter, to reduce expensive memory allocation @@ -3791,23 +4070,6 @@ void GCodeProcessor::post_process() return processed; }; - std::vector filament_mm(m_result.extruders_count, 0.0); - std::vector filament_cm3(m_result.extruders_count, 0.0); - std::vector filament_g(m_result.extruders_count, 0.0); - std::vector filament_cost(m_result.extruders_count, 0.0); - - double filament_total_g = 0.0; - double filament_total_cost = 0.0; - - for (const auto& [id, volume] : m_result.print_statistics.volumes_per_extruder) { - filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); - filament_cm3[id] = volume * 0.001; - filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); - filament_cost[id] = filament_g[id] * double(m_result.filament_cost[id]) * 0.001; - filament_total_g += filament_g[id]; - filament_total_cost += filament_cost[id]; - } - auto process_used_filament = [&](std::string& gcode_line) { // Prefilter for parsing speed. if (gcode_line.size() < 8 || gcode_line[0] != ';' || gcode_line[1] != ' ') @@ -3828,12 +4090,12 @@ void GCodeProcessor::post_process() }; bool ret = false; - ret |= process_tag(gcode_line, "; filament used [mm] =", filament_mm); - ret |= process_tag(gcode_line, "; filament used [g] =", filament_g); - ret |= process_tag(gcode_line, "; total filament used [g] =", { filament_total_g }); - ret |= process_tag(gcode_line, "; filament used [cm3] =", filament_cm3); - ret |= process_tag(gcode_line, "; filament cost =", filament_cost); - ret |= process_tag(gcode_line, "; total filament cost =", { filament_total_cost }); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedMmMask, filament_mm); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedGMask, filament_g); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentUsedGMask, { filament_total_g }); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedCm3Mask, filament_cm3); + ret |= process_tag(gcode_line, PrintStatistics::FilamentCostMask, filament_cost); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentCostMask, { filament_total_cost }); return ret; }; @@ -3972,6 +4234,7 @@ void GCodeProcessor::post_process() }; m_result.lines_ends.clear(); + m_result.lines_ends.emplace_back(std::vector()); unsigned int line_id = 0; // Backtrace data for Tx gcode lines @@ -4041,9 +4304,66 @@ void GCodeProcessor::post_process() export_lines.flush(out, m_result, out_path); + if (m_binarizer.is_enabled()) { + if (m_binarizer.finalize() != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while finalizing the gcode binarizer."); + } + out.close(); in.close(); + if (m_binarizer.is_enabled()) { + // updates m_result.lines_ends from binarized gcode file + m_result.lines_ends.clear(); + + FilePtr file(boost::nowide::fopen(out_path.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + while (res == EResult::Success) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + break; + + // extract lines ends from block + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + for (size_t i = 0; i < block.raw_data.size(); ++i) { + if (block.raw_data[i] == '\n') + lines_ends.emplace_back(i + 1); + } + + if (ftell(file.f) == file_size) + break; + + // read next block header + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success) + break; + if (block_header.type != (uint16_t)EBlockType::GCode) { + res = EResult::InvalidBlockType; + break; + } + } + } + + if (res != EResult::Success && !m_result.lines_ends.empty() && !m_result.lines_ends.front().empty()) + // some error occourred, clear lines ends + m_result.lines_ends = { std::vector() }; + } + } + export_lines.synchronize_moves(m_result); if (rename_file(out_path, m_result.filename)) @@ -4248,7 +4568,7 @@ void GCodeProcessor::simulate_st_synchronize(float additional_time) } } -void GCodeProcessor::update_estimated_times_stats() +void GCodeProcessor::update_estimated_statistics() { auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) { PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast(mode)]; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index af52979bff..103ac79f0d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -13,6 +13,8 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" +#include + #include #include #include @@ -62,16 +64,20 @@ namespace Slic3r { time = 0.0f; travel_time = 0.0f; custom_gcode_times.clear(); + custom_gcode_times.shrink_to_fit(); moves_times.clear(); + moves_times.shrink_to_fit(); roles_times.clear(); + roles_times.shrink_to_fit(); layers_times.clear(); + layers_times.shrink_to_fit(); } }; - std::vector volumes_per_color_change; - std::map volumes_per_extruder; + std::vector volumes_per_color_change; + std::map volumes_per_extruder; std::map> used_filaments_per_role; - std::map cost_per_extruder; + std::map cost_per_extruder; std::array(ETimeMode::Count)> modes; @@ -82,6 +88,7 @@ namespace Slic3r { m.reset(); } volumes_per_color_change.clear(); + volumes_per_color_change.shrink_to_fit(); volumes_per_extruder.clear(); used_filaments_per_role.clear(); cost_per_extruder.clear(); @@ -141,10 +148,13 @@ namespace Slic3r { }; std::string filename; + bool is_binary_file; unsigned int id; std::vector moves; // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. - std::vector lines_ends; + // Binarized gcodes usually have several gcode blocks. Each block has its own list on ends of lines. + // Ascii gcodes have only one list on ends of lines + std::vector> lines_ends; Pointfs bed_shape; float max_print_height; SettingsIds settings_ids; @@ -529,8 +539,12 @@ namespace Slic3r { }; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + static bgcode::binarize::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + private: GCodeReader m_parser; + bgcode::binarize::Binarizer m_binarizer; + static bgcode::binarize::BinarizerConfig s_binarizer_config; EUnits m_units; EPositioningType m_global_positioning_type; @@ -628,6 +642,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } + bgcode::binarize::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::binarize::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { @@ -670,6 +686,9 @@ namespace Slic3r { void apply_config_kissslicer(const std::string& filename); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); + void process_ascii_file(const std::string& filename, std::function cancel_callback = nullptr); + void process_binary_file(const std::string& filename, std::function cancel_callback = nullptr); + // Process tags embedded into comments void process_tags(const std::string_view comment, bool producers_enabled); bool process_producers_tags(const std::string_view comment); @@ -686,8 +705,12 @@ namespace Slic3r { // Move void process_G0(const GCodeReader::GCodeLine& line); void process_G1(const GCodeReader::GCodeLine& line); + enum class G1DiscretizationOrigin { + G1, + G2G3, + }; void process_G1(const std::array, 4>& axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt }, - std::optional feedrate = std::nullopt, std::optional cmt = std::nullopt); + std::optional feedrate = std::nullopt, G1DiscretizationOrigin origin = G1DiscretizationOrigin::G1); // Arc Move void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise); @@ -821,7 +844,7 @@ namespace Slic3r { // Simulates firmware st_synchronize() call void simulate_st_synchronize(float additional_time = 0.0f); - void update_estimated_times_stats(); + void update_estimated_statistics(); double extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section); }; diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp similarity index 86% rename from src/libslic3r/GCodeWriter.cpp rename to src/libslic3r/GCode/GCodeWriter.cpp index fc5adb55a5..628367e396 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -12,12 +12,14 @@ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #include "GCodeWriter.hpp" -#include "CustomGCode.hpp" +#include "../CustomGCode.hpp" + #include #include #include #include #include +#include #ifdef __APPLE__ #include @@ -26,6 +28,8 @@ #define FLAVOR_IS(val) this->config.gcode_flavor == val #define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val +using namespace std::string_view_literals; + namespace Slic3r { // static @@ -103,17 +107,17 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) return {}; - std::string code, comment; + std::string_view code, comment; if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRapFirmware)) { - code = "M109"; - comment = "set temperature and wait for it to be reached"; + code = "M109"sv; + comment = "set temperature and wait for it to be reached"sv; } else { if (FLAVOR_IS(gcfRepRapFirmware)) { // M104 is deprecated on RepRapFirmware - code = "G10"; + code = "G10"sv; } else { - code = "M104"; + code = "M104"sv; } - comment = "set temperature"; + comment = "set temperature"sv; } std::ostringstream gcode; @@ -143,22 +147,22 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait) { if (temperature == m_last_bed_temperature && (! wait || m_last_bed_temperature_reached)) - return std::string(); + return {}; m_last_bed_temperature = temperature; m_last_bed_temperature_reached = wait; - std::string code, comment; + std::string_view code, comment; if (wait && FLAVOR_IS_NOT(gcfTeacup)) { if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) { - code = "M109"; + code = "M109"sv; } else { - code = "M190"; + code = "M190"sv; } - comment = "set bed temperature and wait for it to be reached"; + comment = "set bed temperature and wait for it to be reached"sv; } else { - code = "M140"; - comment = "set bed temperature"; + code = "M140"sv; + comment = "set bed temperature"sv; } std::ostringstream gcode; @@ -189,7 +193,7 @@ std::string GCodeWriter::set_acceleration_internal(Acceleration type, unsigned i auto& last_value = separate_travel ? m_last_travel_acceleration : m_last_acceleration ; if (acceleration == 0 || acceleration == last_value) - return std::string(); + return {}; last_value = acceleration; @@ -258,7 +262,7 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id) return gcode.str(); } -std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const +std::string GCodeWriter::set_speed(double F, const std::string_view comment, const std::string_view cooling_marker) const { assert(F > 0.); assert(F < 100000.); @@ -270,10 +274,9 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s return w.string(); } -std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) +std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment) { - m_pos.x() = point.x(); - m_pos.y() = point.y(); + m_pos.head<2>() = point.head<2>(); GCodeG1Formatter w; w.emit_xy(point); @@ -282,7 +285,40 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com return w.string(); } -std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment) +std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment) +{ + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(ij.x()) < 1200.); + assert(std::abs(ij.y()) < 1200.); + assert(std::abs(ij.x()) >= 0.001 || std::abs(ij.y()) >= 0.001); + + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_ij(ij); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + +std::string GCodeWriter::travel_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, const std::string_view comment) +{ + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) >= 0.001); + assert(std::abs(radius) < 1800.); + + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_radius(radius); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + +std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string_view comment) { // FIXME: This function was not being used when travel_speed_z was separated (bd6badf). // Calculation of feedrate was not updated accordingly. If you want to use @@ -315,7 +351,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co return w.string(); } -std::string GCodeWriter::travel_to_z(double z, const std::string &comment) +std::string GCodeWriter::travel_to_z(double z, const std::string_view comment) { /* If target Z is lower than current Z but higher than nominal Z we don't perform the move but we only adjust the nominal Z by @@ -334,7 +370,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment) return this->_travel_to_z(z, comment); } -std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) +std::string GCodeWriter::_travel_to_z(double z, const std::string_view comment) { m_pos.z() = z; @@ -361,10 +397,12 @@ bool GCodeWriter::will_move_z(double z) const return true; } -std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment) +std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment) { - m_pos.x() = point.x(); - m_pos.y() = point.y(); + assert(dE != 0); + assert(std::abs(dE) < 1000.0); + + m_pos.head<2>() = point.head<2>(); GCodeG1Formatter w; w.emit_xy(point); @@ -373,8 +411,47 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: return w.string(); } +std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment) +{ + assert(std::abs(dE) < 1000.0); + assert(dE != 0); + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(ij.x()) < 1200.); + assert(std::abs(ij.y()) < 1200.); + assert(std::abs(ij.x()) >= 0.001 || std::abs(ij.y()) >= 0.001); + + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_ij(ij); + w.emit_e(m_extrusion_axis, m_extruder->extrude(dE).second); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + +std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment) +{ + assert(dE != 0); + assert(std::abs(dE) < 1000.0); + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) >= 0.001); + assert(std::abs(radius) < 1800.); + + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_radius(radius); + w.emit_e(m_extrusion_axis, m_extruder->extrude(dE).second); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + #if 0 -std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment) +std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment) { m_pos = point; m_lifted = 0; @@ -410,8 +487,11 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe) ); } -std::string GCodeWriter::_retract(double length, double restart_extra, const std::string &comment) +std::string GCodeWriter::_retract(double length, double restart_extra, const std::string_view comment) { + assert(std::abs(length) < 1000.0); + assert(std::abs(restart_extra) < 1000.0); + /* If firmware retraction is enabled, we use a fake value of 1 since we ignore the actual configured retract_length which might be 0, in which case the retraction logic gets skipped. */ diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp similarity index 78% rename from src/libslic3r/GCodeWriter.hpp rename to src/libslic3r/GCode/GCodeWriter.hpp index 94554b087d..1f009f0720 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -9,13 +9,15 @@ #ifndef slic3r_GCodeWriter_hpp_ #define slic3r_GCodeWriter_hpp_ -#include "libslic3r.h" +#include "../libslic3r.h" +#include "../Extruder.hpp" +#include "../Point.hpp" +#include "../PrintConfig.hpp" +#include "CoolingBuffer.hpp" + #include +#include #include -#include "Extruder.hpp" -#include "Point.hpp" -#include "PrintConfig.hpp" -#include "GCode/CoolingBuffer.hpp" namespace Slic3r { @@ -64,13 +66,17 @@ public: // printed with the same extruder. std::string toolchange_prefix() const; std::string toolchange(unsigned int extruder_id); - std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const; - std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()); - std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string()); - std::string travel_to_z(double z, const std::string &comment = std::string()); + std::string set_speed(double F, const std::string_view comment = {}, const std::string_view cooling_marker = {}) const; + std::string travel_to_xy(const Vec2d &point, const std::string_view comment = {}); + std::string travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment = {}); + std::string travel_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, const std::string_view comment = {}); + std::string travel_to_xyz(const Vec3d &point, const std::string_view comment = {}); + std::string travel_to_z(double z, const std::string_view comment = {}); bool will_move_z(double z) const; - std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string()); -// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string()); + std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {}); + std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment); + std::string extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment); +// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {}); std::string retract(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false); std::string unretract(); @@ -121,8 +127,8 @@ private: Print }; - std::string _travel_to_z(double z, const std::string &comment); - std::string _retract(double length, double restart_extra, const std::string &comment); + std::string _travel_to_z(double z, const std::string_view comment); + std::string _retract(double length, double restart_extra, const std::string_view comment); std::string set_acceleration_internal(Acceleration type, unsigned int acceleration); }; @@ -160,6 +166,10 @@ public: static double quantize(double v, size_t ndigits) { return std::round(v * pow_10[ndigits]) * pow_10_inv[ndigits]; } static double quantize_xyzf(double v) { return quantize(v, XYZF_EXPORT_DIGITS); } static double quantize_e(double v) { return quantize(v, E_EXPORT_DIGITS); } + static Vec2d quantize(const Vec2d &pt) + { return { quantize(pt.x(), XYZF_EXPORT_DIGITS), quantize(pt.y(), XYZF_EXPORT_DIGITS) }; } + static Vec3d quantize(const Vec3d &pt) + { return { quantize(pt.x(), XYZF_EXPORT_DIGITS), quantize(pt.y(), XYZF_EXPORT_DIGITS), quantize(pt.z(), XYZF_EXPORT_DIGITS) }; } void emit_axis(const char axis, const double v, size_t digits); @@ -178,7 +188,20 @@ public: this->emit_axis('Z', z, XYZF_EXPORT_DIGITS); } - void emit_e(const std::string &axis, double v) { + void emit_ij(const Vec2d &point) { + if (point.x() != 0) + this->emit_axis('I', point.x(), XYZF_EXPORT_DIGITS); + if (point.y() != 0) + this->emit_axis('J', point.y(), XYZF_EXPORT_DIGITS); + } + + // Positive radius means a smaller arc, + // negative radius means a larger arc. + void emit_radius(const double radius) { + this->emit_axis('R', radius, XYZF_EXPORT_DIGITS); + } + + void emit_e(const std::string_view axis, double v) { if (! axis.empty()) { // not gcfNoExtrusion this->emit_axis(axis[0], v, E_EXPORT_DIGITS); @@ -189,12 +212,12 @@ public: this->emit_axis('F', speed, XYZF_EXPORT_DIGITS); } - void emit_string(const std::string &s) { - strncpy(ptr_err.ptr, s.c_str(), s.size()); + void emit_string(const std::string_view s) { + strncpy(ptr_err.ptr, s.data(), s.size()); ptr_err.ptr += s.size(); } - void emit_comment(bool allow_comments, const std::string &comment) { + void emit_comment(bool allow_comments, const std::string_view comment) { if (allow_comments && ! comment.empty()) { *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' '; this->emit_string(comment); @@ -218,14 +241,25 @@ public: GCodeG1Formatter() { this->buf[0] = 'G'; this->buf[1] = '1'; - this->buf_end = buf + buflen; - this->ptr_err.ptr = this->buf + 2; + this->ptr_err.ptr += 2; } GCodeG1Formatter(const GCodeG1Formatter&) = delete; GCodeG1Formatter& operator=(const GCodeG1Formatter&) = delete; }; +class GCodeG2G3Formatter : public GCodeFormatter { +public: + GCodeG2G3Formatter(bool ccw) { + this->buf[0] = 'G'; + this->buf[1] = ccw ? '3' : '2'; + this->ptr_err.ptr += 2; + } + + GCodeG2G3Formatter(const GCodeG2G3Formatter&) = delete; + GCodeG2G3Formatter& operator=(const GCodeG2G3Formatter&) = delete; +}; + } /* namespace Slic3r */ #endif /* slic3r_GCodeWriter_hpp_ */ diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 4c29ffc893..1381d33b6f 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -35,7 +35,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path) { - BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))); + BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width()))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -49,7 +49,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width())))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -63,7 +63,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width())))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 4185690668..ed0d480027 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1489,7 +1489,7 @@ void SeamPlacer::init(const Print &print, std::function throw_if_can } } -void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, +Point SeamPlacer::place_seam(const Layer *layer, const ExtrusionLoop &loop, bool external_first, const Point &last_pos) const { using namespace SeamPlacerImpl; const PrintObject *po = layer->object(); @@ -1592,7 +1592,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern //lastly, for internal perimeters, do the staggering if requested if (po->config().staggered_inner_seams && loop.length() > 0.0) { //fix depth, it is sometimes strongly underestimated - depth = std::max(loop.paths[projected_point.path_idx].width, depth); + depth = std::max(loop.paths[projected_point.path_idx].width(), depth); while (depth > 0.0f) { auto next_point = get_next_loop_point(projected_point); @@ -1610,14 +1610,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern } } - // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, - // thus empty path segments will not be produced by G-code export. - if (!loop.split_at_vertex(seam_point, scaled(0.0015))) { - // The point is not in the original loop. - // Insert it. - loop.split_at(seam_point, true); - } - + return seam_point; } } // namespace Slic3r diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index e5cb30a607..c4b1edc00b 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -145,7 +145,7 @@ public: void init(const Print &print, std::function throw_if_canceled_func); - void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const; + Point place_seam(const Layer *layer, const ExtrusionLoop &loop, bool external_first, const Point &last_pos) const; private: void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info); diff --git a/src/libslic3r/GCode/SmoothPath.cpp b/src/libslic3r/GCode/SmoothPath.cpp new file mode 100644 index 0000000000..ba793b465e --- /dev/null +++ b/src/libslic3r/GCode/SmoothPath.cpp @@ -0,0 +1,257 @@ +#include "SmoothPath.hpp" + +#include "../ExtrusionEntity.hpp" +#include "../ExtrusionEntityCollection.hpp" + +namespace Slic3r::GCode { + +// Length of a smooth path. +double length(const SmoothPath &path) +{ + double l = 0; + for (const SmoothPathElement &el : path) + l += Geometry::ArcWelder::path_length(el.path); + return l; +} + +// Returns true if the smooth path is longer than a threshold. +bool longer_than(const SmoothPath &path, double length) +{ + for (const SmoothPathElement &el : path) { + for (auto it = std::next(el.path.begin()); it != el.path.end(); ++ it) { + length -= Geometry::ArcWelder::segment_length(*std::prev(it), *it); + if (length < 0) + return true; + } + } + return length < 0; +} + +std::optional sample_path_point_at_distance_from_start(const SmoothPath &path, double distance) +{ + if (distance >= 0) { + for (const SmoothPathElement &el : path) { + auto it = el.path.begin(); + auto end = el.path.end(); + Point prev_point = it->point; + for (++ it; it != end; ++ it) { + Point point = it->point; + if (it->linear()) { + // Linear segment + Vec2d v = (point - prev_point).cast(); + double lsqr = v.squaredNorm(); + if (lsqr > sqr(distance)) + return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + distance -= sqrt(lsqr); + } else { + // Circular segment + float angle = Geometry::ArcWelder::arc_angle(prev_point.cast(), point.cast(), it->radius); + double len = std::abs(it->radius) * angle; + if (len > distance) { + // Rotate the segment end point in reverse towards the start point. + return std::make_optional(prev_point.rotated(- angle * (distance / len), + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + } + distance -= len; + } + if (distance < 0) + return std::make_optional(point); + prev_point = point; + } + } + } + // Failed. + return {}; +} + +std::optional sample_path_point_at_distance_from_end(const SmoothPath &path, double distance) +{ + if (distance >= 0) { + for (const SmoothPathElement& el : path) { + auto it = el.path.begin(); + auto end = el.path.end(); + Point prev_point = it->point; + for (++it; it != end; ++it) { + Point point = it->point; + if (it->linear()) { + // Linear segment + Vec2d v = (point - prev_point).cast(); + double lsqr = v.squaredNorm(); + if (lsqr > sqr(distance)) + return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + distance -= sqrt(lsqr); + } + else { + // Circular segment + float angle = Geometry::ArcWelder::arc_angle(prev_point.cast(), point.cast(), it->radius); + double len = std::abs(it->radius) * angle; + if (len > distance) { + // Rotate the segment end point in reverse towards the start point. + return std::make_optional(prev_point.rotated(-angle * (distance / len), + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + } + distance -= len; + } + if (distance < 0) + return std::make_optional(point); + prev_point = point; + } + } + } + // Failed. + return {}; +} + +double clip_end(SmoothPath &path, double distance) +{ + while (! path.empty() && distance > 0) { + Geometry::ArcWelder::Path &p = path.back().path; + distance = clip_end(p, distance); + if (p.empty()) { + path.pop_back(); + } else { + // Trailing path was trimmed and it is valid. + assert(path.back().path.size() > 1); + assert(distance == 0); + // Distance to go is zero. + return 0; + } + } + // Return distance to go after the whole smooth path was trimmed to zero. + return distance; +} + +void SmoothPathCache::interpolate_add(const ExtrusionPath &path, const InterpolationParameters ¶ms) +{ + double tolerance = params.tolerance; + if (path.role().is_sparse_infill()) + // Use 3x lower resolution than the object fine detail for sparse infill. + tolerance *= 3.; + else if (path.role().is_support()) + // Use 4x lower resolution than the object fine detail for support. + tolerance *= 4.; + else if (path.role().is_skirt()) + // Brim is currently marked as skirt. + // Use 4x lower resolution than the object fine detail for skirt & brim. + tolerance *= 4.; + m_cache[&path.polyline] = Slic3r::Geometry::ArcWelder::fit_path(path.polyline.points, tolerance, params.fit_circle_tolerance); +} + +void SmoothPathCache::interpolate_add(const ExtrusionMultiPath &multi_path, const InterpolationParameters ¶ms) +{ + for (const ExtrusionPath &path : multi_path.paths) + this->interpolate_add(path, params); +} + +void SmoothPathCache::interpolate_add(const ExtrusionLoop &loop, const InterpolationParameters ¶ms) +{ + for (const ExtrusionPath &path : loop.paths) + this->interpolate_add(path, params); +} + +void SmoothPathCache::interpolate_add(const ExtrusionEntityCollection &eec, const InterpolationParameters ¶ms) +{ + for (const ExtrusionEntity *ee : eec) { + if (ee->is_collection()) + this->interpolate_add(*static_cast(ee), params); + else if (const ExtrusionPath *path = dynamic_cast(ee); path) + this->interpolate_add(*path, params); + else if (const ExtrusionMultiPath *multi_path = dynamic_cast(ee); multi_path) + this->interpolate_add(*multi_path, params); + else if (const ExtrusionLoop *loop = dynamic_cast(ee); loop) + this->interpolate_add(*loop, params); + else + assert(false); + } +} + +const Geometry::ArcWelder::Path* SmoothPathCache::resolve(const Polyline *pl) const +{ + auto it = m_cache.find(pl); + return it == m_cache.end() ? nullptr : &it->second; +} + +const Geometry::ArcWelder::Path* SmoothPathCache::resolve(const ExtrusionPath &path) const +{ + return this->resolve(&path.polyline); +} + +Geometry::ArcWelder::Path SmoothPathCache::resolve_or_fit(const ExtrusionPath &path, bool reverse, double tolerance) const +{ + Geometry::ArcWelder::Path out; + if (const Geometry::ArcWelder::Path *cached = this->resolve(path); cached) + out = *cached; + else + out = Geometry::ArcWelder::fit_polyline(path.polyline.points, tolerance); + if (reverse) + Geometry::ArcWelder::reverse(out); + return out; +} + +SmoothPath SmoothPathCache::resolve_or_fit(const ExtrusionPaths &paths, bool reverse, double resolution) const +{ + SmoothPath out; + out.reserve(paths.size()); + if (reverse) { + for (auto it = paths.crbegin(); it != paths.crend(); ++ it) + out.push_back({ it->attributes(), this->resolve_or_fit(*it, true, resolution) }); + } else { + for (auto it = paths.cbegin(); it != paths.cend(); ++ it) + out.push_back({ it->attributes(), this->resolve_or_fit(*it, false, resolution) }); + } + return out; +} + +SmoothPath SmoothPathCache::resolve_or_fit(const ExtrusionMultiPath &multipath, bool reverse, double resolution) const +{ + return this->resolve_or_fit(multipath.paths, reverse, resolution); +} + +SmoothPath SmoothPathCache::resolve_or_fit_split_with_seam( + const ExtrusionLoop &loop, const bool reverse, const double resolution, + const Point &seam_point, const double seam_point_merge_distance_threshold) const +{ + SmoothPath out = this->resolve_or_fit(loop.paths, reverse, resolution); + assert(! out.empty()); + if (! out.empty()) { + // Find a closest point on a vector of smooth paths. + Geometry::ArcWelder::PathSegmentProjection proj; + int proj_path = -1; + for (const SmoothPathElement &el : out) + if (Geometry::ArcWelder::PathSegmentProjection this_proj = Geometry::ArcWelder::point_to_path_projection(el.path, seam_point, proj.distance2); + this_proj.valid()) { + // Found a better (closer) projection. + assert(this_proj.distance2 < proj.distance2); + assert(this_proj.segment_id >= 0 && this_proj.segment_id < el.path.size()); + proj = this_proj; + proj_path = &el - out.data(); + if (proj.distance2 == 0) + // There will be no better split point found than one with zero distance. + break; + } + assert(proj_path >= 0); + // Split the path at the closest point. + Geometry::ArcWelder::Path &path = out[proj_path].path; + std::pair split = Geometry::ArcWelder::split_at( + path, proj, seam_point_merge_distance_threshold); + if (split.second.empty()) { + std::rotate(out.begin(), out.begin() + proj_path + 1, out.end()); + assert(out.back().path == split.first); + } else { + ExtrusionAttributes attr = out[proj_path].path_attributes; + std::rotate(out.begin(), out.begin() + proj_path, out.end()); + out.front().path = std::move(split.second); + if (! split.first.empty()) { + if (out.back().path_attributes == attr) { + // Merge with the last segment. + out.back().path.insert(out.back().path.end(), std::next(split.first.begin()), split.first.end()); + } else + out.push_back({ attr, std::move(split.first) }); + } + } + } + + return out; +} + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/SmoothPath.hpp b/src/libslic3r/GCode/SmoothPath.hpp new file mode 100644 index 0000000000..b2eb335a32 --- /dev/null +++ b/src/libslic3r/GCode/SmoothPath.hpp @@ -0,0 +1,87 @@ +#ifndef slic3r_GCode_SmoothPath_hpp_ +#define slic3r_GCode_SmoothPath_hpp_ + +#include + +#include "../ExtrusionEntity.hpp" +#include "../Geometry/ArcWelder.hpp" + +namespace Slic3r { + +class ExtrusionEntityCollection; + +namespace GCode { + +struct SmoothPathElement +{ + ExtrusionAttributes path_attributes; + Geometry::ArcWelder::Path path; +}; + +using SmoothPath = std::vector; + +// Length of a smooth path. +double length(const SmoothPath &path); +// Returns true if the smooth path is longer than a threshold. +bool longer_than(const SmoothPath &path, const double length); + +std::optional sample_path_point_at_distance_from_start(const SmoothPath &path, double distance); +std::optional sample_path_point_at_distance_from_end(const SmoothPath &path, double distance); + +// Clip end of a smooth path, for seam hiding. +double clip_end(SmoothPath &path, double distance); + +class SmoothPathCache +{ +public: + struct InterpolationParameters { + double tolerance; + double fit_circle_tolerance; + }; + + void interpolate_add(const ExtrusionPath &ee, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionMultiPath &ee, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionLoop &ee, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionEntityCollection &eec, const InterpolationParameters ¶ms); + + const Geometry::ArcWelder::Path* resolve(const Polyline *pl) const; + const Geometry::ArcWelder::Path* resolve(const ExtrusionPath &path) const; + + // Look-up a smooth representation of path in the cache. If it does not exist, produce a simplified polyline. + Geometry::ArcWelder::Path resolve_or_fit(const ExtrusionPath &path, bool reverse, double resolution) const; + + // Look-up a smooth representation of path in the cache. If it does not exist, produce a simplified polyline. + SmoothPath resolve_or_fit(const ExtrusionPaths &paths, bool reverse, double resolution) const; + SmoothPath resolve_or_fit(const ExtrusionMultiPath &path, bool reverse, double resolution) const; + + // Look-up a smooth representation of path in the cache. If it does not exist, produce a simplified polyline. + SmoothPath resolve_or_fit_split_with_seam( + const ExtrusionLoop &path, const bool reverse, const double resolution, + const Point &seam_point, const double seam_point_merge_distance_threshold) const; + +private: + ankerl::unordered_dense::map m_cache; +}; + +// Encapsulates references to global and layer local caches of smooth extrusion paths. +class SmoothPathCaches final +{ +public: + SmoothPathCaches() = delete; + SmoothPathCaches(const SmoothPathCache &global, const SmoothPathCache &layer_local) : + m_global(&global), m_layer_local(&layer_local) {} + SmoothPathCaches operator=(const SmoothPathCaches &rhs) + { m_global = rhs.m_global; m_layer_local = rhs.m_layer_local; return *this; } + + const SmoothPathCache& global() const { return *m_global; } + const SmoothPathCache& layer_local() const { return *m_layer_local; } + +private: + const SmoothPathCache *m_global; + const SmoothPathCache *m_layer_local; +}; + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_SmoothPath_hpp_ diff --git a/src/libslic3r/GCode/Thumbnails.cpp b/src/libslic3r/GCode/Thumbnails.cpp index a11342102c..edafccbea8 100644 --- a/src/libslic3r/GCode/Thumbnails.cpp +++ b/src/libslic3r/GCode/Thumbnails.cpp @@ -9,6 +9,9 @@ #include #include +#include +#include + namespace Slic3r::GCodeThumbnails { using namespace std::literals; @@ -120,4 +123,81 @@ std::unique_ptr compress_thumbnail(const ThumbnailData &d } } +std::pair make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext /*= "PNG"sv*/) +{ + if (thumbnails_string.empty()) + return {}; + + std::istringstream is(thumbnails_string); + std::string point_str; + + ThumbnailErrors errors; + + // generate thumbnails data to process it + + GCodeThumbnailDefinitionsList thumbnails_list; + while (std::getline(is, point_str, ',')) { + Vec2d point(Vec2d::Zero()); + GCodeThumbnailsFormat format; + std::istringstream iss(point_str); + std::string coord_str; + if (std::getline(iss, coord_str, 'x') && !coord_str.empty()) { + std::istringstream(coord_str) >> point(0); + if (std::getline(iss, coord_str, '/') && !coord_str.empty()) { + std::istringstream(coord_str) >> point(1); + + if (0 < point(0) && point(0) < 1000 && 0 < point(1) && point(1) < 1000) { + std::string ext_str; + std::getline(iss, ext_str, '/'); + + if (ext_str.empty()) + ext_str = def_ext.empty() ? "PNG"sv : def_ext; + + // check validity of extention + boost::to_upper(ext_str); + if (!ConfigOptionEnum::from_string(ext_str, format)) { + format = GCodeThumbnailsFormat::PNG; + errors = enum_bitmask(errors | ThumbnailError::InvalidExt); + } + + thumbnails_list.emplace_back(std::make_pair(format, point)); + } + else + errors = enum_bitmask(errors | ThumbnailError::OutOfRange); + continue; + } + } + errors = enum_bitmask(errors | ThumbnailError::InvalidVal); + } + + return std::make_pair(std::move(thumbnails_list), errors); +} + +std::pair make_and_check_thumbnail_list(const ConfigBase& config) +{ + // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // ??? If "thumbnails_format" is not defined, export to PNG. + + // generate thumbnails data to process it + + if (const auto thumbnails_value = config.option("thumbnails")) + return make_and_check_thumbnail_list(thumbnails_value->value); + + return {}; +} + +std::string get_error_string(const ThumbnailErrors& errors) +{ + std::string error_str; + + if (errors.has(ThumbnailError::InvalidVal)) + error_str += "\n - " + format("Invalid input format. Expected vector of dimensions in the following format: \"%1%\"", "XxYxEXT, XxYxEXT, ..."); + if (errors.has(ThumbnailError::OutOfRange)) + error_str += "\n - Input value is out of range"; + if (errors.has(ThumbnailError::InvalidExt)) + error_str += "\n - Some input extention is invalid"; + + return error_str; +} + } // namespace Slic3r::GCodeThumbnails diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 0bfb9e70a1..a5f9803363 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -13,8 +13,18 @@ #include #include +#include + #include +#include "../libslic3r/enum_bitmask.hpp" + +namespace Slic3r { + enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt }; + using ThumbnailErrors = enum_bitmask; + ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError); +} + namespace Slic3r::GCodeThumbnails { struct CompressedImageBuffer @@ -27,35 +37,76 @@ struct CompressedImageBuffer std::unique_ptr compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); +typedef std::vector> GCodeThumbnailDefinitionsList; + +using namespace std::literals; +std::pair make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv); +std::pair make_and_check_thumbnail_list(const ConfigBase &config); + +std::string get_error_string(const ThumbnailErrors& errors); + template -inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector &sizes, GCodeThumbnailsFormat format, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) +inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector>& thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) { // Write thumbnails using base64 encoding if (thumbnail_cb != nullptr) { - static constexpr const size_t max_row_length = 78; - ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true }); - for (const ThumbnailData& data : thumbnails) - if (data.is_valid()) { - auto compressed = compress_thumbnail(data, format); - if (compressed->data && compressed->size) { - std::string encoded; - encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); - encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)compressed->data, compressed->size)); + for (const auto& [format, size] : thumbnails_list) { + static constexpr const size_t max_row_length = 78; + ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ {size}, true, true, true, true }); + for (const ThumbnailData& data : thumbnails) + if (data.is_valid()) { + auto compressed = compress_thumbnail(data, format); + if (compressed->data && compressed->size) { + std::string encoded; + encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); + encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)compressed->data, compressed->size)); - output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size()).str().c_str()); + output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size()).str().c_str()); - while (encoded.size() > max_row_length) { - output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); - encoded = encoded.substr(max_row_length); + while (encoded.size() > max_row_length) { + output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); + encoded = encoded.substr(max_row_length); + } + + if (encoded.size() > 0) + output((boost::format("; %s\n") % encoded).str().c_str()); + + output((boost::format("; %s end\n;\n") % compressed->tag()).str().c_str()); } - - if (encoded.size() > 0) - output((boost::format("; %s\n") % encoded).str().c_str()); - - output((boost::format("; %s end\n;\n") % compressed->tag()).str().c_str()); + throw_if_canceled(); } - throw_if_canceled(); - } + } + } +} + +template +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, + const std::vector> &thumbnails_list, ThrowIfCanceledCallback throw_if_canceled) +{ + using namespace bgcode::core; + using namespace bgcode::binarize; + out_thumbnails.clear(); + assert(thumbnail_cb != nullptr); + if (thumbnail_cb != nullptr) { + for (const auto& [format, size] : thumbnails_list) { + ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ {size}, true, true, true, true }); + for (const ThumbnailData &data : thumbnails) + if (data.is_valid()) { + auto compressed = compress_thumbnail(data, format); + if (compressed->data != nullptr && compressed->size > 0) { + ThumbnailBlock& block = out_thumbnails.emplace_back(ThumbnailBlock()); + block.params.width = (uint16_t)data.width; + block.params.height = (uint16_t)data.height; + switch (format) { + case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)EThumbnailFormat::QOI; break; } + } + block.data.resize(compressed->size); + memcpy(block.data.data(), compressed->data, compressed->size); + } + } + } } } diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp new file mode 100644 index 0000000000..fa726424e9 --- /dev/null +++ b/src/libslic3r/GCode/Wipe.cpp @@ -0,0 +1,260 @@ +#include "Wipe.hpp" +#include "../GCode.hpp" + +#include + +#include + +using namespace std::string_view_literals; + +namespace Slic3r::GCode { + +void Wipe::init(const PrintConfig &config, const std::vector &extruders) +{ + this->reset_path(); + + // Calculate maximum wipe length to accumulate by the wipe cache. + // Paths longer than wipe_xy should never be needed for the wipe move. + double wipe_xy = 0; + const bool multimaterial = extruders.size() > 1; + for (auto id : extruders) + if (config.wipe.get_at(id)) { + // Wipe length to extrusion ratio. + const double xy_to_e = this->calc_xy_to_e_ratio(config, id); + wipe_xy = std::max(wipe_xy, xy_to_e * config.retract_length.get_at(id)); + if (multimaterial) + wipe_xy = std::max(wipe_xy, xy_to_e * config.retract_length_toolchange.get_at(id)); + } + + if (wipe_xy == 0) + this->disable(); + else + this->enable(wipe_xy); +} + +void Wipe::set_path(SmoothPath &&path, bool reversed) +{ + this->reset_path(); + + if (this->enabled() && ! path.empty()) { + if (reversed) { + m_path = std::move(path.back().path); + Geometry::ArcWelder::reverse(m_path); + int64_t len = Geometry::ArcWelder::estimate_path_length(m_path); + for (auto it = std::next(path.rbegin()); len < m_wipe_len_max && it != path.rend(); ++ it) { + if (it->path_attributes.role.is_bridge()) + break; // Do not perform a wipe on bridges. + assert(it->path.size() >= 2); + assert(m_path.back().point == it->path.back().point); + if (m_path.back().point != it->path.back().point) + // ExtrusionMultiPath is interrupted in some place. This should not really happen. + break; + len += Geometry::ArcWelder::estimate_path_length(it->path); + m_path.insert(m_path.end(), it->path.rbegin() + 1, it->path.rend()); + } + } else { + m_path = std::move(path.front().path); + int64_t len = Geometry::ArcWelder::estimate_path_length(m_path); + for (auto it = std::next(path.begin()); len < m_wipe_len_max && it != path.end(); ++ it) { + if (it->path_attributes.role.is_bridge()) + break; // Do not perform a wipe on bridges. + assert(it->path.size() >= 2); + assert(m_path.back().point == it->path.front().point); + if (m_path.back().point != it->path.front().point) + // ExtrusionMultiPath is interrupted in some place. This should not really happen. + break; + len += Geometry::ArcWelder::estimate_path_length(it->path); + m_path.insert(m_path.end(), it->path.begin() + 1, it->path.end()); + } + } + } + + assert(m_path.empty() || m_path.size() > 1); +} + +std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) +{ + std::string gcode; + const Extruder &extruder = *gcodegen.writer().extruder(); + static constexpr const std::string_view wipe_retract_comment = "wipe and retract"sv; + + // Remaining quantized retraction length. + if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length()); + retract_length > 0 && this->has_path()) { + // Delayed emitting of a wipe start tag. + bool wiped = false; + const double wipe_speed = this->calc_wipe_speed(gcodegen.writer().config); + auto start_wipe = [&wiped, &gcode, &gcodegen, wipe_speed](){ + if (! wiped) { + wiped = true; + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; + gcode += gcodegen.writer().set_speed(wipe_speed * 60, {}, gcodegen.enable_cooling_markers() ? ";_WIPE"sv : ""sv); + } + }; + const double xy_to_e = this->calc_xy_to_e_ratio(gcodegen.writer().config, extruder.id()); + auto wipe_linear = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev_quantized, Vec2d &p) { + Vec2d p_quantized = GCodeFormatter::quantize(p); + if (p_quantized == prev_quantized) { + p = p_quantized; + return false; + } + double segment_length = (p_quantized - prev_quantized).norm(); + // Quantize E axis as it is to be extruded as a whole segment. + double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); + bool done = false; + if (dE > retract_length - EPSILON) { + if (dE > retract_length + EPSILON) + // Shorten the segment. + p = GCodeFormatter::quantize(Vec2d(prev_quantized + (p - prev_quantized) * (retract_length / dE))); + else + p = p_quantized; + dE = retract_length; + done = true; + } else + p = p_quantized; + gcode += gcodegen.writer().extrude_to_xy(p, -dE, wipe_retract_comment); + retract_length -= dE; + return done; + }; + const bool emit_radius = gcodegen.config().arc_fitting == ArcFittingType::EmitRadius; + auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius, &wipe_linear]( + const Vec2d &prev_quantized, Vec2d &p, double radius_in, const bool ccw) { + Vec2d p_quantized = GCodeFormatter::quantize(p); + if (p_quantized == prev_quantized) { + p = p_quantized; + return false; + } + // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. + double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in; + if (radius == 0) + // Degenerated arc after quantization. Process it as if it was a line segment. + return wipe_linear(prev_quantized, p); + Vec2d center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p_quantized.cast(), double(radius), ccw); + float angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p_quantized.cast(), double(radius)); + assert(angle > 0); + double segment_length = angle * std::abs(radius); + double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); + bool done = false; + if (dE > retract_length - EPSILON) { + if (dE > retract_length + EPSILON) { + // Shorten the segment. Recalculate the arc from the unquantized end coordinate. + center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p.cast(), double(radius), ccw); + angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p.cast(), double(radius)); + segment_length = angle * std::abs(radius); + dE = xy_to_e * segment_length; + p = GCodeFormatter::quantize( + Vec2d(center + Eigen::Rotation2D((ccw ? angle : -angle) * (retract_length / dE)) * (prev_quantized - center))); + } else + p = p_quantized; + dE = retract_length; + done = true; + } else + p = p_quantized; + assert(dE > 0); + if (emit_radius) { + gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment); + } else { + // Calculate quantized IJ circle center offset. + Vec2d ij = GCodeFormatter::quantize(Vec2d(center - prev_quantized)); + if (ij == Vec2d::Zero()) + // Degenerated arc after quantization. Process it as if it was a line segment. + return wipe_linear(prev_quantized, p); + // The arc is valid. + gcode += gcodegen.writer().extrude_to_xy_G2G3IJ( + p, ij, ccw, -dE, wipe_retract_comment); + } + retract_length -= dE; + return done; + }; + // Start with the current position, which may be different from the wipe path start in case of loop clipping. + Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos()); + auto it = this->path().begin(); + Vec2d p = gcodegen.point_to_gcode(it->point + m_offset); + ++ it; + bool done = false; + if (p != prev) { + start_wipe(); + done = wipe_linear(prev, p); + } + if (! done) { + prev = p; + auto end = this->path().end(); + for (; it != end && ! done; ++ it) { + p = gcodegen.point_to_gcode(it->point + m_offset); + if (p != prev) { + start_wipe(); + if (it->linear() ? + wipe_linear(prev, p) : + wipe_arc(prev, p, unscaled(it->radius), it->ccw())) + break; + prev = p; + } + } + } + if (wiped) { + // add tag for processor + assert(p == GCodeFormatter::quantize(p)); + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; + gcodegen.set_last_pos(gcodegen.gcode_to_point(p)); + } + } + + // Prevent wiping again on the same path. + this->reset_path(); + return gcode; +} + +// Make a little move inwards before leaving loop after path was extruded, +// thus the current extruder position is at the end of a path and the path +// may not be closed in case the loop was clipped to hide a seam. +std::optional wipe_hide_seam(const SmoothPath &path, bool is_hole, double wipe_length) +{ + assert(! path.empty()); + assert(path.front().path.size() >= 2); + assert(path.back().path.size() >= 2); + + // Heuristics for estimating whether there is a chance that the wipe move will fit inside a small perimeter + // or that the wipe move direction could be calculated with reasonable accuracy. + if (longer_than(path, 2.5 * wipe_length)) { + // The print head will be moved away from path end inside the island. + Point p_current = path.back().path.back().point; + Point p_next = path.front().path.front().point; + Point p_prev; + { + // Is the seam hiding gap large enough already? + double l = wipe_length - (p_next - p_current).cast().norm(); + if (l > 0) { + // Not yet. + std::optional n = sample_path_point_at_distance_from_start(path, l); + assert(n); + if (! n) + // Wipe move cannot be calculated, the loop is not long enough. This should not happen due to the longer_than() test above. + return {}; + } + if (std::optional p = sample_path_point_at_distance_from_end(path, wipe_length); p) + p_prev = *p; + else + // Wipe move cannot be calculated, the loop is not long enough. This should not happen due to the longer_than() test above. + return {}; + } + // Detect angle between last and first segment. + // The side depends on the original winding order of the polygon (left for contours, right for holes). + double angle_inside = angle(p_next - p_current, p_prev - p_current); + assert(angle_inside >= -M_PI && angle_inside <= M_PI); + // 3rd of this angle will be taken, thus make the angle monotonic before interpolation. + if (is_hole) { + if (angle_inside > 0) + angle_inside -= 2.0 * M_PI; + } else { + if (angle_inside < 0) + angle_inside += 2.0 * M_PI; + } + // Rotate the forward segment inside by 1/3 of the wedge angle. + auto v_rotated = Eigen::Rotation2D(angle_inside) * (p_next - p_current).cast().normalized(); + return std::make_optional(p_current + (v_rotated * wipe_length).cast()); + } + + return {}; +} + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/Wipe.hpp b/src/libslic3r/GCode/Wipe.hpp new file mode 100644 index 0000000000..f02a77fa7f --- /dev/null +++ b/src/libslic3r/GCode/Wipe.hpp @@ -0,0 +1,73 @@ +#ifndef slic3r_GCode_Wipe_hpp_ +#define slic3r_GCode_Wipe_hpp_ + +#include "SmoothPath.hpp" + +#include "../Geometry/ArcWelder.hpp" +#include "../Point.hpp" +#include "../PrintConfig.hpp" + +#include +#include + +namespace Slic3r { + +class GCodeGenerator; + +namespace GCode { + +class Wipe { +public: + using Path = Slic3r::Geometry::ArcWelder::Path; + + Wipe() = default; + + void init(const PrintConfig &config, const std::vector &extruders); + void enable(double wipe_len_max) { m_enabled = true; m_wipe_len_max = wipe_len_max; } + void disable() { m_enabled = false; } + bool enabled() const { return m_enabled; } + + const Path& path() const { return m_path; } + bool has_path() const { assert(m_path.empty() || m_path.size() > 1); return ! m_path.empty(); } + void reset_path() { m_path.clear(); m_offset = Point::Zero(); } + void set_path(const Path &path) { + assert(path.empty() || path.size() > 1); + this->reset_path(); + if (this->enabled() && path.size() > 1) + m_path = path; + } + void set_path(Path &&path) { + assert(path.empty() || path.size() > 1); + this->reset_path(); + if (this->enabled() && path.size() > 1) + m_path = std::move(path); + } + void set_path(SmoothPath &&path, bool reversed); + void offset_path(const Point &v) { m_offset += v; } + + std::string wipe(GCodeGenerator &gcodegen, bool toolchange); + + // Reduce feedrate a bit; travel speed is often too high to move on existing material. + // Too fast = ripping of existing material; too slow = short wipe path, thus more blob. + static double calc_wipe_speed(const GCodeConfig &config) { return config.travel_speed.value * 0.8; } + // Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one + // due to rounding (TODO: test and/or better math for this). + static double calc_xy_to_e_ratio(const GCodeConfig &config, unsigned int extruder_id) + { return 0.95 * floor(config.retract_speed.get_at(extruder_id) + 0.5) / calc_wipe_speed(config); } + +private: + bool m_enabled{ false }; + // Maximum length of a path to accumulate. Only wipes shorter than this threshold will be requested. + double m_wipe_len_max{ 0. }; + Path m_path; + // Offset from m_path to the current PrintObject active. + Point m_offset{ Point::Zero() }; +}; + +// Make a little move inwards before leaving loop. +std::optional wipe_hide_seam(const SmoothPath &path, bool is_hole, double wipe_length); + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_Wipe_hpp_ diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index ca80dd3dc2..464d0fe755 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1008,7 +1008,7 @@ void WipeTower::toolchange_Change( // This is where we want to place the custom gcodes. We will use placeholders for this. // These will be substituted by the actual gcodes when the gcode is generated. //writer.append("[end_filament_gcode]\n"); - writer.append("[toolchange_gcode]\n"); + writer.append("[toolchange_gcode_from_wipe_tower_generator]\n"); // Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc) // gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 43997396c6..ff2fabdf0f 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -2,8 +2,8 @@ ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ -#ifndef WipeTower_ -#define WipeTower_ +#ifndef slic3r_GCode_WipeTower_hpp_ +#define slic3r_GCode_WipeTower_hpp_ #include #include @@ -415,4 +415,4 @@ private: } // namespace Slic3r -#endif // WipeTowerPrusaMM_hpp_ +#endif // slic3r_GCode_WipeTower_hpp_ diff --git a/src/libslic3r/GCode/WipeTowerIntegration.cpp b/src/libslic3r/GCode/WipeTowerIntegration.cpp new file mode 100644 index 0000000000..eb06ddd165 --- /dev/null +++ b/src/libslic3r/GCode/WipeTowerIntegration.cpp @@ -0,0 +1,250 @@ +#include "WipeTowerIntegration.hpp" + +#include "../GCode.hpp" +#include "../libslic3r.h" + +#include "boost/algorithm/string/replace.hpp" + +namespace Slic3r::GCode { + +static inline Point wipe_tower_point_to_object_point(GCodeGenerator &gcodegen, const Vec2f& wipe_tower_pt) +{ + return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); +} + +std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const +{ + if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) + throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); + + std::string gcode; + + // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) + // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position + float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); + + auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f { + Vec2f out = Eigen::Rotation2Df(alpha) * pt; + out += m_wipe_tower_pos; + return out; + }; + + Vec2f start_pos = tcr.start_pos; + Vec2f end_pos = tcr.end_pos; + if (! tcr.priming) { + start_pos = transform_wt_pt(start_pos); + end_pos = transform_wt_pt(end_pos); + } + + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + float wipe_tower_rotation = tcr.priming ? 0.f : alpha; + + std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); + + gcode += gcodegen.writer().unlift(); // Make sure there is no z-hop (in most cases, there isn't). + + double current_z = gcodegen.writer().get_position().z(); + if (z == -1.) // in case no specific z was provided, print at current_z pos + z = current_z; + + const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id); + const bool will_go_down = ! is_approx(z, current_z); + const bool is_ramming = (gcodegen.config().single_extruder_multi_material) + || (! gcodegen.config().single_extruder_multi_material && gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool)); + const bool should_travel_to_tower = ! tcr.priming + && (tcr.force_travel // wipe tower says so + || ! needs_toolchange // this is just finishing the tower with no toolchange + || is_ramming); + if (should_travel_to_tower) { + gcode += gcodegen.retract(); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + gcode += gcodegen.travel_to( + wipe_tower_point_to_object_point(gcodegen, start_pos), + ExtrusionRole::Mixed, + "Travel to a Wipe Tower"); + gcode += gcodegen.unretract(); + } else { + // When this is multiextruder printer without any ramming, we can just change + // the tool without travelling to the tower. + } + + if (will_go_down) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); + gcode += gcodegen.writer().unretract(); + } + + std::string toolchange_gcode_str; + std::string deretraction_str; + if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) { + if (is_ramming) + gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines. + toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z + if (gcodegen.config().wipe_tower) + deretraction_str = gcodegen.unretract(); + } + assert(toolchange_gcode_str.empty() || toolchange_gcode_str.back() == '\n'); + assert(deretraction_str.empty() || deretraction_str.back() == '\n'); + + // Insert the toolchange and deretraction gcode into the generated gcode. + boost::replace_first(tcr_rotated_gcode, "[toolchange_gcode_from_wipe_tower_generator]", toolchange_gcode_str); + boost::replace_first(tcr_rotated_gcode, "[deretraction_from_wipe_tower_generator]", deretraction_str); + std::string tcr_gcode; + unescape_string_cstyle(tcr_rotated_gcode, tcr_gcode); + gcode += tcr_gcode; + + // A phony move to the end position at the wipe tower. + gcodegen.writer().travel_to_xy(end_pos.cast()); + gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); + if (!is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); + gcode += gcodegen.writer().unretract(); + } + + else { + // Prepare a future wipe. + // Convert to a smooth path. + Geometry::ArcWelder::Path path; + path.reserve(tcr.wipe_path.size()); + std::transform(tcr.wipe_path.begin(), tcr.wipe_path.end(), std::back_inserter(path), + [&gcodegen, &transform_wt_pt](const Vec2f &wipe_pt) { + return Geometry::ArcWelder::Segment{ wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt)) }; + }); + // Pass to the wipe cache. + gcodegen.m_wipe.set_path(std::move(path)); + } + + // Let the planner know we are traveling between objects. + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + return gcode; +} + +// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode +// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) +std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const +{ + Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); + + std::istringstream gcode_str(tcr.gcode); + std::string gcode_out; + std::string line; + Vec2f pos = tcr.start_pos; + Vec2f transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; + Vec2f old_pos(-1000.1f, -1000.1f); + + while (gcode_str) { + std::getline(gcode_str, line); // we read the gcode line by line + + // All G1 commands should be translated and rotated. X and Y coords are + // only pushed to the output when they differ from last time. + // WT generator can override this by appending the never_skip_tag + if (boost::starts_with(line, "G1 ")) { + bool never_skip = false; + auto it = line.find(WipeTower::never_skip_tag()); + if (it != std::string::npos) { + // remove the tag and remember we saw it + never_skip = true; + line.erase(it, it + WipeTower::never_skip_tag().size()); + } + std::ostringstream line_out; + std::istringstream line_str(line); + line_str >> std::noskipws; // don't skip whitespace + char ch = 0; + line_str >> ch >> ch; // read the "G1" + while (line_str >> ch) { + if (ch == 'X' || ch == 'Y') + line_str >> (ch == 'X' ? pos.x() : pos.y()); + else + line_out << ch; + } + + transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; + + if (transformed_pos != old_pos || never_skip) { + line = line_out.str(); + boost::trim_left(line); // Remove leading spaces + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) << "G1"; + if (transformed_pos.x() != old_pos.x() || never_skip) + oss << " X" << transformed_pos.x() - extruder_offset.x(); + if (transformed_pos.y() != old_pos.y() || never_skip) + oss << " Y" << transformed_pos.y() - extruder_offset.y(); + if (! line.empty()) + oss << " "; + line = oss.str() + line; + old_pos = transformed_pos; + } + } + + gcode_out += line + "\n"; + + // If this was a toolchange command, we should change current extruder offset + if (line == "[toolchange_gcode]") { + extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); + + // If the extruder offset changed, add an extra move so everything is continuous + if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) + << "G1 X" << transformed_pos.x() - extruder_offset.x() + << " Y" << transformed_pos.y() - extruder_offset.y() + << "\n"; + gcode_out += oss.str(); + } + } + } + return gcode_out; +} + + +std::string WipeTowerIntegration::prime(GCodeGenerator &gcodegen) +{ + std::string gcode; + for (const WipeTower::ToolChangeResult& tcr : m_priming) { + if (! tcr.extrusions.empty()) + gcode += append_tcr(gcodegen, tcr, tcr.new_tool); + } + return gcode; +} + +std::string WipeTowerIntegration::tool_change(GCodeGenerator &gcodegen, int extruder_id, bool finish_layer) +{ + std::string gcode; + assert(m_layer_idx >= 0); + if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { + if (m_layer_idx < (int)m_tool_changes.size()) { + if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) + throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); + + // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, + // resulting in a wipe tower with sparse layers. + double wipe_tower_z = -1; + bool ignore_sparse = false; + if (gcodegen.config().wipe_tower_no_sparse_layers.value) { + wipe_tower_z = m_last_wipe_tower_print_z; + ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool && m_layer_idx != 0); + if (m_tool_change_idx == 0 && !ignore_sparse) + wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; + } + + if (!ignore_sparse) { + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); + m_last_wipe_tower_print_z = wipe_tower_z; + } + } + } + return gcode; +} + +// Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. +std::string WipeTowerIntegration::finalize(GCodeGenerator &gcodegen) +{ + std::string gcode; + if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) + gcode += gcodegen.change_layer(m_final_purge.print_z); + gcode += append_tcr(gcodegen, m_final_purge, -1); + return gcode; +} + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/WipeTowerIntegration.hpp b/src/libslic3r/GCode/WipeTowerIntegration.hpp new file mode 100644 index 0000000000..52b27625bb --- /dev/null +++ b/src/libslic3r/GCode/WipeTowerIntegration.hpp @@ -0,0 +1,65 @@ +#ifndef slic3r_GCode_WipeTowerIntegration_hpp_ +#define slic3r_GCode_WipeTowerIntegration_hpp_ + +#include "WipeTower.hpp" +#include "../PrintConfig.hpp" + +namespace Slic3r { + +class GCodeGenerator; + +namespace GCode { + +class WipeTowerIntegration { +public: + WipeTowerIntegration( + const PrintConfig &print_config, + const std::vector &priming, + const std::vector> &tool_changes, + const WipeTower::ToolChangeResult &final_purge) : + m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), + m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), + m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), + m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), + m_extruder_offsets(print_config.extruder_offset.values), + m_priming(priming), + m_tool_changes(tool_changes), + m_final_purge(final_purge), + m_layer_idx(-1), + m_tool_change_idx(0) + {} + + std::string prime(GCodeGenerator &gcodegen); + void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } + std::string tool_change(GCodeGenerator &gcodegen, int extruder_id, bool finish_layer); + std::string finalize(GCodeGenerator &gcodegen); + std::vector used_filament_length() const; + +private: + WipeTowerIntegration& operator=(const WipeTowerIntegration&); + std::string append_tcr(GCodeGenerator &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; + + // Postprocesses gcode: rotates and moves G1 extrusions and returns result + std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; + + // Left / right edges of the wipe tower, for the planning of wipe moves. + const float m_left; + const float m_right; + const Vec2f m_wipe_tower_pos; + const float m_wipe_tower_rotation; + const std::vector m_extruder_offsets; + + // Reference to cached values at the Printer class. + const std::vector &m_priming; + const std::vector> &m_tool_changes; + const WipeTower::ToolChangeResult &m_final_purge; + // Current layer index. + int m_layer_idx; + int m_tool_change_idx; + double m_last_wipe_tower_print_z = 0.f; +}; + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_WipeTowerIntegration_hpp_ diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 2faba880ce..8938d3e22b 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -199,10 +199,11 @@ bool GCodeReader::parse_file(const std::string &file, callback_t callback) return this->parse_file_internal(file, callback, [](size_t){}); } -bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector &lines_ends) +bool GCodeReader::parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends) { lines_ends.clear(); - return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); }); + lines_ends.push_back(std::vector()); + return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos) { lines_ends.front().emplace_back(file_pos); }); } bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback) diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index ebaaff9772..1207f03912 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -135,7 +135,7 @@ public: bool parse_file(const std::string &file, callback_t callback); // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code // as an overlay in the 3D scene. - bool parse_file(const std::string &file, callback_t callback, std::vector &lines_ends); + bool parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends); // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed. bool parse_file_raw(const std::string &file, raw_line_callback_t callback); diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp new file mode 100644 index 0000000000..1bb9fea77d --- /dev/null +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -0,0 +1,674 @@ +// The following code for merging circles into arches originates from https://github.com/FormerLurker/ArcWelderLib + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Library +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Uses the 'Gcode Processor Library' for gcode parsing, position processing, logging, and other various functionality. +// +// Copyright(C) 2021 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "ArcWelder.hpp" +#include "Circle.hpp" + +#include "../MultiPoint.hpp" +#include "../Polygon.hpp" + +#include +#include +#include + +namespace Slic3r { namespace Geometry { namespace ArcWelder { + +Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation) +{ + Vec2d center = arc_center(p1.cast(), p2.cast(), radius, ccw); + double angle = arc_angle(p1.cast(), p2.cast(), radius); + assert(angle > 0); + + double r = std::abs(radius); + size_t num_steps = arc_discretization_steps(r, angle, deviation); + double angle_step = angle / num_steps; + + Points out; + out.reserve(num_steps + 1); + out.emplace_back(p1); + if (! ccw) + angle_step *= -1.; + for (size_t i = 1; i < num_steps; ++ i) + out.emplace_back(p1.rotated(angle_step * i, center.cast())); + out.emplace_back(p2); + return out; +} + +struct Circle +{ + Point center; + double radius; +}; + +// Interpolate three points with a circle. +// Returns false if the three points are collinear or if the radius is bigger than maximum allowed radius. +static std::optional try_create_circle(const Point &p1, const Point &p2, const Point &p3, const double max_radius) +{ + if (auto center = Slic3r::Geometry::try_circle_center(p1.cast(), p2.cast(), p3.cast(), SCALED_EPSILON); center) { + Point c = center->cast(); + if (double r = sqrt(double((c - p1).cast().squaredNorm())); r <= max_radius) + return std::make_optional({ c, float(r) }); + } + return {}; +} + +// Returns a closest point on the segment. +// Returns false if the closest point is not inside the segment, but at its boundary. +static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &pt, Point &out) +{ + Vec2i64 v21 = (p2 - p1).cast(); + int64_t l2 = v21.squaredNorm(); + if (l2 > int64_t(SCALED_EPSILON)) { + if (int64_t t = (pt - p1).cast().dot(v21); + t >= int64_t(SCALED_EPSILON) && t < l2 - int64_t(SCALED_EPSILON)) { + out = p1 + ((double(t) / double(l2)) * v21.cast()).cast(); + return true; + } + } + // The segment is short or the closest point is an end point. + return false; +} + +static inline bool circle_approximation_sufficient(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance) +{ + // The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated. + assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); + assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); + assert(end - begin >= 3); + + // Test the 1st point. + if (double distance_from_center = (*begin - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; + + for (auto it = std::next(begin); it != end; ++ it) { + if (double distance_from_center = (*it - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; + Point closest_point; + if (foot_pt_on_segment(*std::prev(it), *it, circle.center, closest_point)) { + if (double distance_from_center = (closest_point - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; + } + } + return true; +} + +static inline bool get_deviation_sum_squared(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance, double &total_deviation) +{ + // The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated. + assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); + assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); + assert(end - begin >= 3); + + total_deviation = 0; + + const double tolerance2 = sqr(tolerance); + for (auto it = std::next(begin); std::next(it) != end; ++ it) + if (double deviation2 = sqr((*it - circle.center).cast().norm() - circle.radius); deviation2 > tolerance2) + return false; + else + total_deviation += deviation2; + + for (auto it = begin; std::next(it) != end; ++ it) { + Point closest_point; + if (foot_pt_on_segment(*it, *std::next(it), circle.center, closest_point)) { + if (double deviation2 = sqr((closest_point - circle.center).cast().norm() - circle.radius); deviation2 > tolerance2) + return false; + else + total_deviation += deviation2; + } + } + + return true; +} + +static std::optional try_create_circle(const Points::const_iterator begin, const Points::const_iterator end, const double max_radius, const double tolerance) +{ + std::optional out; + size_t size = end - begin; + if (size == 3) { + out = try_create_circle(*begin, *std::next(begin), *std::prev(end), max_radius); + if (out && ! circle_approximation_sufficient(*out, begin, end, tolerance)) + out.reset(); + } else { +#if 0 + size_t ipivot = size / 2; + // Take a center difference of points at the center of the path. + //FIXME does it really help? For short arches, the linear interpolation may be + Point pivot = (size % 2 == 0) ? (*(begin + ipivot) + *(begin + ipivot - 1)) / 2 : + (*(begin + ipivot - 1) + *(begin + ipivot + 1)) / 2; + if (std::optional circle = try_create_circle(*begin, pivot, *std::prev(end), max_radius); + circle && circle_approximation_sufficient(*circle, begin, end, tolerance)) + return circle; +#endif + // Find the circle with the least deviation, if one exists. + double least_deviation = std::numeric_limits::max(); + double current_deviation; + for (auto it = std::next(begin); std::next(it) != end; ++ it) + if (std::optional circle = try_create_circle(*begin, *it, *std::prev(end), max_radius); + circle && get_deviation_sum_squared(*circle, begin, end, tolerance, current_deviation)) { + if (current_deviation < least_deviation) { + out = circle; + least_deviation = current_deviation; + } + } + } + return out; +} + +// ported from ArcWelderLib/ArcWelder/segmented/shape.h class "arc" +class Arc { +public: + Point start_point; + Point end_point; + Point center; + double radius; + Orientation direction { Orientation::Unknown }; +}; + +static inline int sign(const int64_t i) +{ + return i > 0 ? 1 : i < 0 ? -1 : 0; +} + +// Return orientation of a polyline with regard to the center. +// Successive points are expected to take less than a PI angle step. +Orientation arc_orientation( + const Point ¢er, + const Points::const_iterator begin, + const Points::const_iterator end) +{ + assert(end - begin >= 3); + // Assumption: Two successive points of a single segment span an angle smaller than PI. + Vec2i64 vstart = (*begin - center).cast(); + Vec2i64 vprev = vstart; + int arc_dir = 0; + for (auto it = std::next(begin); it != end; ++ it) { + Vec2i64 v = (*it - center).cast(); + int dir = sign(cross2(vprev, v)); + if (dir == 0) { + // Ignore radial segments. + } else if (arc_dir * dir < 0) { + // The path turns back and overextrudes. Such path is likely invalid, but the arc interpolation should + // rather maintain such an invalid path instead of covering it up. + // Don't replace such a path with an arc. + return {}; + } else { + // Success, either establishing the direction for the first time, or moving in the same direction as the last time. + arc_dir = dir; + vprev = v; + } + } + return arc_dir == 0 ? + // All points are radial wrt. the center, this is unexpected. + Orientation::Unknown : + // Arc is valid, either CCW or CW. + arc_dir > 0 ? Orientation::CCW : Orientation::CW; +} + +static inline std::optional try_create_arc_impl( + const Circle &circle, + const Points::const_iterator begin, + const Points::const_iterator end, + const double tolerance, + const double path_tolerance_percent) +{ + assert(end - begin >= 3); + // Assumption: Two successive points of a single segment span an angle smaller than PI. + Orientation orientation = arc_orientation(circle.center, begin, end); + if (orientation == Orientation::Unknown) + return {}; + + Vec2i64 vstart = (*begin - circle.center).cast(); + Vec2i64 vend = (*std::prev(end) - circle.center).cast(); + double angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); + if (orientation == Orientation::CW) + angle *= -1.; + if (angle < 0) + angle += 2. * M_PI; + assert(angle >= 0. && angle < 2. * M_PI + EPSILON); + + // Check the length against the original length. + // This can trigger simply due to the differing path lengths + // but also could indicate that the vector calculation above + // got wrong direction + const double arc_length = circle.radius * angle; + const double approximate_length = length(begin, end); + assert(approximate_length > 0); + const double arc_length_difference_relative = (arc_length - approximate_length) / approximate_length; + + if (std::fabs(arc_length_difference_relative) >= path_tolerance_percent) { + return {}; + } else { + assert(circle_approximation_sufficient(circle, begin, end, tolerance + SCALED_EPSILON)); + return std::make_optional(Arc{ + *begin, + *std::prev(end), + circle.center, + angle > M_PI ? - circle.radius : circle.radius, + orientation + }); + } +} + +static inline std::optional try_create_arc( + const Points::const_iterator begin, + const Points::const_iterator end, + double max_radius = default_scaled_max_radius, + double tolerance = default_scaled_resolution, + double path_tolerance_percent = default_arc_length_percent_tolerance) +{ + std::optional circle = try_create_circle(begin, end, max_radius, tolerance); + if (! circle) + return {}; + return try_create_arc_impl(*circle, begin, end, tolerance, path_tolerance_percent); +} + +float arc_angle(const Vec2f &start_pos, const Vec2f &end_pos, Vec2f ¢er_pos, bool is_ccw) +{ + if ((end_pos - start_pos).squaredNorm() < sqr(1e-6)) { + // If start equals end, full circle is considered. + return float(2. * M_PI); + } else { + Vec2f v1 = start_pos - center_pos; + Vec2f v2 = end_pos - center_pos; + if (! is_ccw) + std::swap(v1, v2); + float radian = atan2(cross2(v1, v2), v1.dot(v2)); + return radian < 0 ? float(2. * M_PI) + radian : radian; + } +} + +float arc_length(const Vec2f &start_pos, const Vec2f &end_pos, Vec2f ¢er_pos, bool is_ccw) +{ + return (center_pos - start_pos).norm() * arc_angle(start_pos, end_pos, center_pos, is_ccw); +} + +// Reduces polyline in the (begin, end, begin, tolerance, [](const Segment &s) { return s.point; }); +} + +Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tolerance) +{ + assert(tolerance >= 0); + assert(fit_circle_percent_tolerance >= 0); + + Path out; + out.reserve(src.size()); + if (tolerance <= 0 || src.size() <= 2) { + // No simplification, just convert. + std::transform(src.begin(), src.end(), std::back_inserter(out), [](const Point &p) -> Segment { return { p }; }); + } else if (fit_circle_percent_tolerance <= 0) { + // Convert and simplify to a polyline. + std::transform(src.begin(), src.end(), std::back_inserter(out), [](const Point &p) -> Segment { return { p }; }); + out.erase(douglas_peucker_in_place(out.begin(), out.end(), tolerance), out.end()); + } else { + // Perform simplification & fitting. + // Index of the start of a last polyline, which has not yet been decimated. + int begin_pl_idx = 0; + out.push_back({ src.front(), 0.f }); + for (auto it = std::next(src.begin()); it != src.end();) { + // Minimum 2 additional points required for circle fitting. + auto begin = std::prev(it); + auto end = std::next(it); + assert(end <= src.end()); + std::optional arc; + while (end != src.end()) { + auto next_end = std::next(end); + if (std::optional this_arc = try_create_arc( + begin, next_end, + ArcWelder::default_scaled_max_radius, + tolerance, fit_circle_percent_tolerance); + this_arc) { + assert(this_arc->direction != Orientation::Unknown); + arc = this_arc; + end = next_end; + } else + break; + } + if (arc) { + // If there is a trailing polyline, decimate it first before saving a new arc. + if (out.size() - begin_pl_idx > 2) { + // Decimating linear segmens only. + assert(std::all_of(out.begin() + begin_pl_idx + 1, out.end(), [](const Segment &seg) { return seg.linear(); })); + out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); + assert(out.back().linear()); + } + // Save the index of an end of the new circle segment, which may become the first point of a possible future polyline. + begin_pl_idx = int(out.size()); + // This will be the next point to try to add. + it = end; + // Add the new arc. + assert(*begin == arc->start_point); + assert(*std::prev(it) == arc->end_point); + assert(out.back().point == arc->start_point); + out.push_back({ arc->end_point, float(arc->radius), arc->direction }); +#if 0 + // Verify that all the source points are at tolerance distance from the interpolated path. + { + const Segment &seg_start = *std::prev(std::prev(out.end())); + const Segment &seg_end = out.back(); + const Vec2d center = arc_center(seg_start.point.cast(), seg_end.point.cast(), double(seg_end.radius), seg_end.ccw()); + assert(seg_start.point == *begin); + assert(seg_end.point == *std::prev(end)); + assert(arc_orientation(center.cast(), begin, end) == arc->direction); + for (auto it = std::next(begin); it != end; ++ it) { + Point ptstart = *std::prev(it); + Point ptend = *it; + Point closest_point; + if (foot_pt_on_segment(ptstart, ptend, center.cast(), closest_point)) { + double distance_from_center = (closest_point.cast() - center).norm(); + assert(std::abs(distance_from_center - std::abs(seg_end.radius)) < tolerance + SCALED_EPSILON); + } + Vec2d v = (ptend - ptstart).cast(); + double len = v.norm(); + auto num_segments = std::min(10, ceil(2. * len / fit_circle_percent_tolerance)); + for (size_t i = 0; i < num_segments; ++ i) { + Point p = ptstart + (v * (double(i) / double(num_segments))).cast(); + assert(i == 0 || inside_arc_wedge(seg_start.point.cast(), seg_end.point.cast(), center, seg_end.radius > 0, seg_end.ccw(), p.cast())); + double d2 = sqr((p.cast() - center).norm() - std::abs(seg_end.radius)); + assert(d2 < sqr(tolerance + SCALED_EPSILON)); + } + } + } +#endif + } else { + // Arc is not valid, append a linear segment. + out.push_back({ *it ++ }); + } + } + if (out.size() - begin_pl_idx > 2) + // Do the final polyline decimation. + out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); + } + +#if 0 + // Verify that all the source points are at tolerance distance from the interpolated path. + for (auto it = std::next(src.begin()); it != src.end(); ++ it) { + Point start = *std::prev(it); + Point end = *it; + Vec2d v = (end - start).cast(); + double len = v.norm(); + auto num_segments = std::min(10, ceil(2. * len / fit_circle_percent_tolerance)); + for (size_t i = 0; i <= num_segments; ++ i) { + Point p = start + (v * (double(i) / double(num_segments))).cast(); + PathSegmentProjection proj = point_to_path_projection(out, p); + assert(proj.valid()); + assert(proj.distance2 < sqr(tolerance + SCALED_EPSILON)); + } + } +#endif + + return out; +} + +void reverse(Path &path) +{ + if (path.size() > 1) { + auto prev = path.begin(); + for (auto it = std::next(prev); it != path.end(); ++ it) { + prev->radius = it->radius; + prev->orientation = it->orientation == Orientation::CCW ? Orientation::CW : Orientation::CCW; + prev = it; + } + path.back().radius = 0; + std::reverse(path.begin(), path.end()); + } +} + +double clip_start(Path &path, const double len) +{ + reverse(path); + double remaining = clip_end(path, len); + reverse(path); + // Return remaining distance to go. + return remaining; +} + +double clip_end(Path &path, double distance) +{ + while (distance > 0) { + const Segment last = path.back(); + path.pop_back(); + if (path.empty()) + break; + if (last.linear()) { + // Linear segment + Vec2d v = (path.back().point - last.point).cast(); + double lsqr = v.squaredNorm(); + if (lsqr > sqr(distance)) { + path.push_back({ last.point + (v * (distance / sqrt(lsqr))).cast() }); + // Length to go is zero. + return 0; + } + distance -= sqrt(lsqr); + } else { + // Circular segment + double angle = arc_angle(path.back().point.cast(), last.point.cast(), last.radius); + double len = std::abs(last.radius) * angle; + if (len > distance) { + // Rotate the segment end point in reverse towards the start point. + if (last.ccw()) + angle *= -1.; + path.push_back({ + last.point.rotated(angle * (distance / len), + arc_center(path.back().point.cast(), last.point.cast(), double(last.radius), last.ccw()).cast()), + last.radius, last.orientation }); + // Length to go is zero. + return 0; + } + distance -= len; + } + } + + // Return remaining distance to go. + assert(distance >= 0); + return distance; +} + +PathSegmentProjection point_to_path_projection(const Path &path, const Point &point, double search_radius2) +{ + assert(path.size() != 1); + // initialized to "invalid" state. + PathSegmentProjection out; + out.distance2 = search_radius2; + if (path.size() < 2 || path.front().point == point) { + // First point is the closest point. + if (path.empty()) { + // No closest point available. + } else if (const Point p0 = path.front().point; p0 == point) { + out.segment_id = 0; + out.point = p0; + out.distance2 = 0; + } else if (double d2 = (p0 - point).cast().squaredNorm(); d2 < out.distance2) { + out.segment_id = 0; + out.point = p0; + out.distance2 = d2; + } + } else { + assert(path.size() >= 2); + // min_point_it will contain an end point of a segment with a closest projection found + // or path.cbegin() if no such closest projection closer than search_radius2 was found. + auto min_point_it = path.cbegin(); + Point prev = path.front().point; + for (auto it = std::next(path.cbegin()); it != path.cend(); ++ it) { + if (it->linear()) { + // Linear segment + Point proj; + // distance_to_squared() will possibly return the start or end point of a line segment. + if (double d2 = line_alg::distance_to_squared(Line(prev, it->point), point, &proj); d2 < out.distance2) { + out.point = proj; + out.distance2 = d2; + min_point_it = it; + } + } else { + // Circular arc + Vec2i64 center = arc_center(prev.cast(), it->point.cast(), double(it->radius), it->ccw()).cast(); + // Test whether point is inside the wedge. + Vec2i64 v1 = prev.cast() - center; + Vec2i64 v2 = it->point.cast() - center; + Vec2i64 vp = point.cast() - center; + if (inside_arc_wedge_vectors(v1, v2, it->radius > 0, it->ccw(), vp)) { + // Distance of the radii. + const auto r = double(std::abs(it->radius)); + const auto rtest = sqrt(double(vp.squaredNorm())); + if (double d2 = sqr(rtest - r); d2 < out.distance2) { + if (rtest > SCALED_EPSILON) + // Project vp to the arc. + out.point = center.cast() + (vp.cast() * (r / rtest)).cast(); + else + // Test point is very close to the center of the radius. Any point of the arc is the closest. + // Pick the start. + out.point = prev; + out.distance2 = d2; + out.center = center.cast(); + min_point_it = it; + } + } else { + // Distance to the start point. + if (double d2 = double((v1 - vp).squaredNorm()); d2 < out.distance2) { + out.point = prev; + out.distance2 = d2; + min_point_it = it; + } + } + } + prev = it->point; + } + if (! path.back().linear()) { + // Calculate distance to the end point. + if (double d2 = (path.back().point - point).cast().squaredNorm(); d2 < out.distance2) { + out.point = path.back().point; + out.distance2 = d2; + min_point_it = std::prev(path.end()); + } + } + // If a new closes point was found, it is closer than search_radius2. + assert((min_point_it == path.cbegin()) == (out.distance2 == search_radius2)); + // Output is not valid yet. + assert(! out.valid()); + if (min_point_it != path.cbegin()) { + // Make it valid by setting the segment. + out.segment_id = std::prev(min_point_it) - path.begin(); + assert(out.valid()); + } + } + + assert(! out.valid() || (out.segment_id >= 0 && out.segment_id < path.size())); + return out; +} + +std::pair split_at(const Path &path, const PathSegmentProjection &proj, const double min_segment_length) +{ + assert(proj.valid()); + assert(! proj.valid() || (proj.segment_id >= 0 && proj.segment_id < path.size())); + assert(path.size() > 1); + std::pair out; + if (! proj.valid() || proj.segment_id + 1 == path.size() || (proj.segment_id + 2 == path.size() && proj.point == path.back().point)) + out.first = path; + else if (proj.segment_id == 0 && proj.point == path.front().point) + out.second = path; + else { + // Path will likely be split to two pieces. + assert(proj.valid() && proj.segment_id >= 0 && proj.segment_id + 1 < path.size()); + const Segment &start = path[proj.segment_id]; + const Segment &end = path[proj.segment_id + 1]; + bool split_segment = true; + int split_segment_id = proj.segment_id; + if (int64_t d2 = (proj.point - start.point).cast().squaredNorm(); d2 < sqr(min_segment_length)) { + split_segment = false; + int64_t d22 = (proj.point - end.point).cast().squaredNorm(); + if (d22 < d2) + // Split at the end of the segment. + ++ split_segment_id; + } else if (int64_t d2 = (proj.point - end.point).cast().squaredNorm(); d2 < sqr(min_segment_length)) { + ++ split_segment_id; + split_segment = false; + } + if (split_segment) { + out.first.assign(path.begin(), path.begin() + split_segment_id + 2); + out.second.assign(path.begin() + split_segment_id, path.end()); + assert(out.first[out.first.size() - 2] == start); + assert(out.first.back() == end); + assert(out.second.front() == start); + assert(out.second[1] == end); + assert(out.first.size() + out.second.size() == path.size() + 2); + assert(out.first.back().radius == out.second[1].radius); + out.first.back().point = proj.point; + out.second.front().point = proj.point; + if (end.radius < 0) { + // A large arc (> PI) was split. + // At least one of the two arches that were created by splitting the original arch will become smaller. + // Make the radii of those arches that became < PI positive. + // In case of a projection onto an arc, proj.center should be filled in and valid. + auto vstart = (start.point - proj.center).cast(); + auto vend = (end.point - proj.center).cast(); + auto vproj = (proj.point - proj.center).cast(); + if ((cross2(vstart, vproj) > 0) == end.ccw()) + // Make the radius of a minor arc positive. + out.first.back().radius *= -1.f; + if ((cross2(vproj, vend) > 0) == end.ccw()) + // Make the radius of a minor arc positive. + out.second[1].radius *= -1.f; + assert(out.first.size() > 1); + assert(out.second.size() > 1); + out.second.front().radius = 0; + } + } else { + assert(split_segment_id >= 0 && split_segment_id < path.size()); + if (split_segment_id + 1 == int(path.size())) + out.first = path; + else if (split_segment_id == 0) + out.second = path; + else { + // Split at the start of proj.segment_id. + out.first.assign(path.begin(), path.begin() + split_segment_id + 1); + out.second.assign(path.begin() + split_segment_id, path.end()); + assert(out.first.size() + out.second.size() == path.size() + 1); + assert(out.first.back() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.second.front() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.first.size() > 1); + assert(out.second.size() > 1); + out.second.front().radius = 0; + } + } + } + + return out; +} + +std::pair split_at(const Path &path, const Point &point, const double min_segment_length) +{ + return split_at(path, point_to_path_projection(path, point), min_segment_length); +} + +} } } // namespace Slic3r::Geometry::ArcWelder diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp new file mode 100644 index 0000000000..dfc6d886e6 --- /dev/null +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -0,0 +1,324 @@ +#ifndef slic3r_Geometry_ArcWelder_hpp_ +#define slic3r_Geometry_ArcWelder_hpp_ + +#include + +#include "../Point.hpp" + +namespace Slic3r { namespace Geometry { namespace ArcWelder { + +// Calculate center point of an arc given two points and a radius. +// positive radius: take shorter arc +// negative radius: take longer arc +// radius must NOT be zero! +template +inline Eigen::Matrix arc_center( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const Float radius, + const bool is_ccw) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_center(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_center(): second parameter is not a 2D vector"); + static_assert(std::is_same::value, "arc_center(): Both vectors must be of the same type."); + static_assert(std::is_same::value, "arc_center(): Radius must be of the same type as the vectors."); + assert(radius != 0); + using Vector = Eigen::Matrix; + auto v = end_pos - start_pos; + Float q2 = v.squaredNorm(); + assert(q2 > 0); + Float t2 = sqr(radius) / q2 - Float(.25f); + // If the start_pos and end_pos are nearly antipodal, t2 may become slightly negative. + // In that case return a centroid of start_point & end_point. + Float t = t2 > 0 ? sqrt(t2) : Float(0); + auto mid = Float(0.5) * (start_pos + end_pos); + Vector vp{ -v.y() * t, v.x() * t }; + return (radius > Float(0)) == is_ccw ? (mid + vp).eval() : (mid - vp).eval(); +} + +// Calculate angle of an arc given two points and a radius. +// Returned angle is in the range <0, 2 PI) +// positive radius: take shorter arc +// negative radius: take longer arc +// radius must NOT be zero! +template +inline typename Derived::Scalar arc_angle( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const typename Derived::Scalar radius) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_angle(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_angle(): second parameter is not a 2D vector"); + static_assert(std::is_same::value, "arc_angle(): Both vectors must be of the same type."); + assert(radius != 0); + using Float = typename Derived::Scalar; + Float a = Float(0.5) * (end_pos - start_pos).norm() / radius; + return radius > Float(0) ? + // acute angle: + (a > Float( 1.) ? Float(M_PI) : Float(2.) * std::asin(a)) : + // obtuse angle: + (a < Float(-1.) ? Float(M_PI) : Float(2. * M_PI) + Float(2.) * std::asin(a)); +} + +// Calculate positive length of an arc given two points and a radius. +// positive radius: take shorter arc +// negative radius: take longer arc +// radius must NOT be zero! +template +inline typename Derived::Scalar arc_length( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const typename Derived::Scalar radius) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_length(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): second parameter is not a 2D vector"); + static_assert(std::is_same::value, "arc_length(): Both vectors must be of the same type."); + assert(radius != 0); + return arc_angle(start_pos, end_pos, radius) * std::abs(radius); +} + +// Calculate positive length of an arc given two points, center and orientation. +template +inline typename Derived::Scalar arc_length( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const Eigen::MatrixBase ¢er_pos, + const bool ccw) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_length(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): second parameter is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): third parameter is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value, "arc_length(): All third points must be of the same type."); + using Float = typename Derived::Scalar; + auto vstart = start_pos - center_pos; + auto vend = end_pos - center_pos; + Float radius = vstart.norm(); + Float angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); + if (! ccw) + angle *= Float(-1.); + if (angle < 0) + angle += Float(2. * M_PI); + assert(angle >= Float(0.) && angle < Float(2. * M_PI + EPSILON)); + return angle * radius; +} + +// Test whether a point is inside a wedge of an arc. +template +inline bool inside_arc_wedge_vectors( + const Eigen::MatrixBase &start_vec, + const Eigen::MatrixBase &end_vec, + const bool shorter_arc, + const bool ccw, + const Eigen::MatrixBase &query_vec) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "inside_arc_wedge_vectors(): start_vec is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "inside_arc_wedge_vectors(): end_vec is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "inside_arc_wedge_vectors(): query_vec is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value, "inside_arc_wedge_vectors(): All vectors must be of the same type."); + return shorter_arc ? + // Smaller (convex) wedge. + (ccw ? + cross2(start_vec, query_vec) > 0 && cross2(query_vec, end_vec) > 0 : + cross2(start_vec, query_vec) < 0 && cross2(query_vec, end_vec) < 0) : + // Larger (concave) wedge. + (ccw ? + cross2(end_vec, query_vec) < 0 || cross2(query_vec, start_vec) < 0 : + cross2(end_vec, query_vec) > 0 || cross2(query_vec, start_vec) > 0); +} + +template +inline bool inside_arc_wedge( + const Eigen::MatrixBase &start_pt, + const Eigen::MatrixBase &end_pt, + const Eigen::MatrixBase ¢er_pt, + const bool shorter_arc, + const bool ccw, + const Eigen::MatrixBase &query_pt) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "inside_arc_wedge(): start_pt is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "inside_arc_wedge(): end_pt is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "inside_arc_wedge(): center_pt is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived4::SizeAtCompileTime) == 2, "inside_arc_wedge(): query_pt is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value && + std::is_same::value, "inside_arc_wedge(): All vectors must be of the same type."); + return inside_arc_wedge_vectors(start_pt - center_pt, end_pt - center_pt, shorter_arc, ccw, query_pt - center_pt); +} + +template +inline bool inside_arc_wedge( + const Eigen::MatrixBase &start_pt, + const Eigen::MatrixBase &end_pt, + const Float radius, + const bool ccw, + const Eigen::MatrixBase &query_pt) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "inside_arc_wedge(): start_pt is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "inside_arc_wedge(): end_pt is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "inside_arc_wedge(): query_pt is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value && + std::is_same::value, "inside_arc_wedge(): All vectors + radius must be of the same type."); + return inside_arc_wedge(start_pt, end_pt, + arc_center(start_pt, end_pt, radius, ccw), + radius > 0, ccw, query_pt); +} + +// Return number of linear segments necessary to interpolate arc of a given positive radius and positive angle to satisfy +// maximum deviation of an interpolating polyline from an analytic arc. +template +size_t arc_discretization_steps(const FloatType radius, const FloatType angle, const FloatType deviation) +{ + assert(radius > 0); + assert(angle > 0); + assert(angle <= FloatType(2. * M_PI)); + assert(deviation > 0); + + FloatType d = radius - deviation; + return d < EPSILON ? + // Radius smaller than deviation. + ( // Acute angle: a single segment interpolates the arc with sufficient accuracy. + angle < M_PI || + // Obtuse angle: Test whether the furthest point (center) of an arc is closer than deviation to the center of a line segment. + radius * (FloatType(1.) + cos(M_PI - FloatType(.5) * angle)) < deviation ? + // Single segment is sufficient + 1 : + // Two segments are necessary, the middle point is at the center of the arc. + 2) : + size_t(ceil(angle / (2. * acos(d / radius)))); +} + +// Discretize arc given the radius, orientation and maximum deviation from the arc. +// Returned polygon starts with p1, ends with p2 and it is discretized to guarantee the maximum deviation. +Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation); + +// 1.2m diameter, maximum given by coord_t +static constexpr const double default_scaled_max_radius = scaled(600.); +// 0.05mm +static constexpr const double default_scaled_resolution = scaled(0.05); +// 5 percent +static constexpr const double default_arc_length_percent_tolerance = 0.05; + +enum class Orientation : unsigned char { + Unknown, + CCW, + CW, +}; + +// Returns orientation of a polyline with regard to the center. +// Successive points are expected to take less than a PI angle step. +// Returns Orientation::Unknown if the orientation with regard to the center +// is not monotonous. +Orientation arc_orientation( + const Point ¢er, + const Points::const_iterator begin, + const Points::const_iterator end); + +// Single segment of a smooth path. +struct Segment +{ + // End point of a linear or circular segment. + // Start point is provided by the preceding segment. + Point point; + // Radius of a circular segment. Positive - take the shorter arc. Negative - take the longer arc. Zero - linear segment. + float radius{ 0.f }; + // CCW or CW. Ignored for zero radius (linear segment). + Orientation orientation{ Orientation::CCW }; + + bool linear() const { return radius == 0; } + bool ccw() const { return orientation == Orientation::CCW; } + bool cw() const { return orientation == Orientation::CW; } +}; + +inline bool operator==(const Segment &lhs, const Segment &rhs) { + return lhs.point == rhs.point && lhs.radius == rhs.radius && lhs.orientation == rhs.orientation; +} + +using Segments = std::vector; +using Path = Segments; + +// Interpolate polyline path with a sequence of linear / circular segments given the interpolation tolerance. +// Only convert to polyline if zero tolerance. +// Convert to polyline and decimate polyline if zero fit_circle_percent_tolerance. +// Path fitting is inspired with the arc fitting algorithm in +// Arc Welder: Anti-Stutter Library by Brad Hochgesang FormerLurker@pm.me +// https://github.com/FormerLurker/ArcWelderLib +Path fit_path(const Points &points, double tolerance, double fit_circle_percent_tolerance); + +// Decimate polyline into a smooth path structure using Douglas-Peucker polyline decimation algorithm. +inline Path fit_polyline(const Points &points, double tolerance) { return fit_path(points, tolerance, 0.); } + +template +inline FloatType segment_length(const Segment &start, const Segment &end) +{ + return end.linear() ? + (end.point - start.point).cast().norm() : + arc_length(start.point.cast(), end.point.cast(), FloatType(end.radius)); +} + +template +inline FloatType path_length(const Path &path) +{ + FloatType len = 0; + for (size_t i = 1; i < path.size(); ++ i) + len += segment_length(path[i - 1], path[i]); + return len; +} + +// Estimate minimum path length of a segment cheaply without having to calculate center of an arc and it arc length. +// Used for caching a smooth path chunk that is certainly longer than a threshold. +inline int64_t estimate_min_segment_length(const Segment &start, const Segment &end) +{ + if (end.linear() || end.radius > 0) { + // Linear segment or convex wedge, take the larger X or Y component. + Point v = (end.point - start.point).cwiseAbs(); + return std::max(v.x(), v.y()); + } else { + // Arc with angle > PI. + // Returns estimate of PI * r + return - int64_t(3) * int64_t(end.radius); + } +} + +// Estimate minimum path length cheaply without having to calculate center of an arc and it arc length. +// Used for caching a smooth path chunk that is certainly longer than a threshold. +inline int64_t estimate_path_length(const Path &path) +{ + int64_t len = 0; + for (size_t i = 1; i < path.size(); ++ i) + len += Geometry::ArcWelder::estimate_min_segment_length(path[i - 1], path[i]); + return len; +} + +void reverse(Path &path); + +// Clip start / end of a smooth path by len. +// If path is shorter than len, remaining path length to trim will be returned. +double clip_start(Path &path, const double len); +double clip_end(Path &path, const double len); + +struct PathSegmentProjection +{ + // Start segment of a projection on the path. + size_t segment_id { std::numeric_limits::max() }; + // Projection of the point on the segment. + Point point { 0, 0 }; + // If the point lies on an arc, the arc center is cached here. + Point center { 0, 0 }; + // Square of a distance of the projection. + double distance2 { std::numeric_limits::max() }; + + bool valid() const { return this->segment_id != std::numeric_limits::max(); } +}; +// Returns closest segment and a parameter along the closest segment of a path to a point. +PathSegmentProjection point_to_path_projection(const Path &path, const Point &point, double search_radius2 = std::numeric_limits::max()); +// Split a path into two paths at a segment point. Snap to an existing point if the projection of "point is closer than min_segment_length. +std::pair split_at(const Path &path, const PathSegmentProjection &proj, const double min_segment_length); +// Split a path into two paths at a point closest to "point". Snap to an existing point if the projection of "point is closer than min_segment_length. +std::pair split_at(const Path &path, const Point &point, const double min_segment_length); + +} } } // namespace Slic3r::Geometry::ArcWelder + +#endif // slic3r_Geometry_ArcWelder_hpp_ diff --git a/src/libslic3r/Geometry/Circle.cpp b/src/libslic3r/Geometry/Circle.cpp index 012b240f8a..cdaf72d6a8 100644 --- a/src/libslic3r/Geometry/Circle.cpp +++ b/src/libslic3r/Geometry/Circle.cpp @@ -21,9 +21,14 @@ Point circle_center_taubin_newton(const Points::const_iterator& input_begin, con return Point::new_scale(center.x(), center.y()); } -/// Adapted from work in "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126 -/// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end -/// lie on. +// Robust and accurate algebraic circle fit, which works well even if data points are observed only within a small arc. +// The method was proposed by G. Taubin in +// "Estimation Of Planar Curves, Surfaces And Nonplanar Space Curves Defined By Implicit Equations, +// With Applications To Edge And Range Image Segmentation", IEEE Trans. PAMI, Vol. 13, pages 1115-1138, (1991)." +// This particular implementation was adapted from +// "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126" +// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end +// lie on. Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles) { // calculate the centroid of the data set diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index a192cc2fd6..f4bca148d7 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -13,10 +13,17 @@ namespace Slic3r { namespace Geometry { // https://en.wikipedia.org/wiki/Circumscribed_circle // Circumcenter coordinates, Cartesian coordinates -template -Vector circle_center(const Vector &a, const Vector &bsrc, const Vector &csrc, typename Vector::Scalar epsilon) +// In case the three points are collinear, returns their centroid. +template +Eigen::Matrix circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename Derived::Scalar epsilon) { - using Scalar = typename Vector::Scalar; + static_assert(Derived ::IsVectorAtCompileTime && int(Derived ::SizeAtCompileTime) == 2, "circle_center(): 1st point is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "circle_center(): 2nd point is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "circle_center(): 3rd point is not a 2D vector"); + static_assert(std::is_same::value && std::is_same::value, + "circle_center(): All three points must be of the same type."); + using Scalar = typename Derived::Scalar; + using Vector = Eigen::Matrix; Vector b = bsrc - a; Vector c = csrc - a; Scalar lb = b.squaredNorm(); @@ -34,6 +41,32 @@ Vector circle_center(const Vector &a, const Vector &bsrc, const Vector &csrc, ty } } +// https://en.wikipedia.org/wiki/Circumscribed_circle +// Circumcenter coordinates, Cartesian coordinates +// Returns no value if the three points are collinear. +template +std::optional> try_circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename Derived::Scalar epsilon) +{ + static_assert(Derived ::IsVectorAtCompileTime && int(Derived ::SizeAtCompileTime) == 2, "try_circle_center(): 1st point is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "try_circle_center(): 2nd point is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "try_circle_center(): 3rd point is not a 2D vector"); + static_assert(std::is_same::value && std::is_same::value, + "try_circle_center(): All three points must be of the same type."); + using Scalar = typename Derived::Scalar; + using Vector = Eigen::Matrix; + Vector b = bsrc - a; + Vector c = csrc - a; + Scalar lb = b.squaredNorm(); + Scalar lc = c.squaredNorm(); + if (Scalar d = b.x() * c.y() - b.y() * c.x(); std::abs(d) < epsilon) { + // The three points are collinear. + return {}; + } else { + Vector v = lc * b - lb * c; + return std::make_optional(a + Vector(- v.y(), v.x()) / (2 * d)); + } +} + // 2D circle defined by its center and squared radius template struct CircleSq { @@ -69,7 +102,7 @@ struct Circle { Vector center; Scalar radius; - Circle() {} + Circle() = default; Circle(const Vector ¢er, const Scalar radius) : center(center), radius(radius) {} Circle(const Vector &a, const Vector &b) : center(Scalar(0.5) * (a + b)) { radius = (a - center).norm(); } Circle(const Vector &a, const Vector &b, const Vector &c, const Scalar epsilon) { *this = CircleSq(a, b, c, epsilon); } diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 97962a5a0e..2e6156a88e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -189,7 +189,7 @@ void MeasuringImpl::update_planes() int neighbor_idx = face_neighbors[facets[face_id]][edge_id]; if (neighbor_idx == -1) goto PLANE_FAILURE; - if (visited[face_id][edge_id] || (int)face_to_plane[neighbor_idx] == plane_id) { + if (visited[face_id][edge_id] || face_to_plane[neighbor_idx] == plane_id) { visited[face_id][edge_id] = true; continue; } @@ -223,7 +223,7 @@ void MeasuringImpl::update_planes() // Remember all halfedges we saw to break out of such infinite loops. boost::container::small_vector he_seen; - while ( (int)face_to_plane[sm.face(he)] == plane_id && he != he_orig) { + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) { he_seen.emplace_back(he); he = sm.next_around_target(he); if (he.is_invalid() || std::find(he_seen.begin(), he_seen.end(), he) != he_seen.end()) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index af2299d024..d636c41fb6 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -40,7 +40,7 @@ #include "SVG.hpp" #include -#include "GCodeWriter.hpp" +#include "GCode/GCodeWriter.hpp" namespace Slic3r { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index f7d67fa30d..b8cb2b4912 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -294,25 +294,26 @@ struct CutConnector float height; float radius_tolerance;// [0.f : 1.f] float height_tolerance;// [0.f : 1.f] + float z_angle {0.f}; CutConnectorAttributes attribs; CutConnector() - : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) + : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f), z_angle(0.f) {} - CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) - : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, float za, CutConnectorAttributes attributes) + : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), z_angle(za), attribs(attributes) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.z_angle, rhs.attribs) {} bool operator==(const CutConnector& other) const; bool operator!=(const CutConnector& other) const { return !(other == (*this)); } template inline void serialize(Archive& ar) { - ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); + ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, z_angle, attribs); } }; diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 9c74ad42cd..5c7b62f3a1 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -108,100 +108,6 @@ bool MultiPoint::remove_duplicate_points() return false; } -Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) -{ - Points result_pts; - auto tolerance_sq = int64_t(sqr(tolerance)); - if (! pts.empty()) { - const Point *anchor = &pts.front(); - size_t anchor_idx = 0; - const Point *floater = &pts.back(); - size_t floater_idx = pts.size() - 1; - result_pts.reserve(pts.size()); - result_pts.emplace_back(*anchor); - if (anchor_idx != floater_idx) { - assert(pts.size() > 1); - std::vector dpStack; - dpStack.reserve(pts.size()); - dpStack.emplace_back(floater_idx); - for (;;) { - int64_t max_dist_sq = 0; - size_t furthest_idx = anchor_idx; - // find point furthest from line seg created by (anchor, floater) and note it - { - const Point a = *anchor; - const Point f = *floater; - const Vec2i64 v = (f - a).cast(); - if (const int64_t l2 = v.squaredNorm(); l2 == 0) { - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) - if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; - } - } else { - const double dl2 = double(l2); - const Vec2d dv = v.cast(); - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - const Point p = pts[i]; - const Vec2i64 va = (p - a).template cast(); - const int64_t t = va.dot(v); - int64_t dist_sq; - if (t <= 0) { - dist_sq = va.squaredNorm(); - } else if (t >= l2) { - dist_sq = (p - f).cast().squaredNorm(); - } else { - const Vec2i64 w = ((double(t) / dl2) * dv).cast(); - dist_sq = (w - va).squaredNorm(); - } - if (dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; - } - } - } - } - // remove point if less than tolerance - if (max_dist_sq <= tolerance_sq) { - result_pts.emplace_back(*floater); - anchor_idx = floater_idx; - anchor = floater; - assert(dpStack.back() == floater_idx); - dpStack.pop_back(); - if (dpStack.empty()) - break; - floater_idx = dpStack.back(); - } else { - floater_idx = furthest_idx; - dpStack.emplace_back(floater_idx); - } - floater = &pts[floater_idx]; - } - } - assert(result_pts.front() == pts.front()); - assert(result_pts.back() == pts.back()); - -#if 0 - { - static int iRun = 0; - BoundingBox bbox(pts); - BoundingBox bbox2(result_pts); - bbox.merge(bbox2); - SVG svg(debug_out_path("douglas_peucker_%d.svg", iRun ++).c_str(), bbox); - if (pts.front() == pts.back()) - svg.draw(Polygon(pts), "black"); - else - svg.draw(Polyline(pts), "black"); - if (result_pts.front() == result_pts.back()) - svg.draw(Polygon(result_pts), "green", scale_(0.1)); - else - svg.draw(Polyline(result_pts), "green", scale_(0.1)); - } -#endif - } - return result_pts; -} - // Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825 // thanks to @fuchstraumer /* @@ -224,7 +130,7 @@ struct vis_node{ // other node if it's area is less than the other node's area bool operator<(const vis_node& other) { return (this->area < other.area); } }; -Points MultiPoint::visivalingam(const Points& pts, const double& tolerance) +Points MultiPoint::visivalingam(const Points &pts, const double tolerance) { // Make sure there's enough points in "pts" to bother with simplification. assert(pts.size() >= 2); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index d7c28b300b..9040217628 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -17,6 +17,123 @@ namespace Slic3r { class BoundingBox; class BoundingBox3; +// Reduces polyline in the +inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, const double tolerance, PointGetter point_getter) +{ + using InputIteratorCategory = typename std::iterator_traits::iterator_category; + static_assert(std::is_base_of_v); + using Vector = Eigen::Matrix; + if (begin != end) { + // Supporting in-place reduction and the data type may be generic, thus we are always making a copy of the point value before there is a chance + // to override input by moving the data to the output. + auto a = point_getter(*begin); + *out ++ = std::move(*begin); + if (auto next = std::next(begin); next == end) { + // Single point input only. + } else if (std::next(next) == end) { + // Two points input. + *out ++ = std::move(*next); + } else { + const auto tolerance_sq = SquareLengthType(sqr(tolerance)); + InputIterator anchor = begin; + InputIterator floater = std::prev(end); + std::vector dpStack; + if constexpr (std::is_base_of_v) + dpStack.reserve(end - begin); + dpStack.emplace_back(floater); + auto f = point_getter(*floater); + for (;;) { + assert(anchor != floater); + bool take_floater = false; + InputIterator furthest = anchor; + if (std::next(anchor) == floater) { + // Two point segment. Accept the floater. + take_floater = true; + } else { + SquareLengthType max_dist_sq = 0; + // Find point furthest from line seg created by (anchor, floater) and note it. + const Vector v = (f - a).template cast(); + if (const SquareLengthType l2 = v.squaredNorm(); l2 == 0) { + // Zero length segment, find the furthest point between anchor and floater. + for (auto it = std::next(anchor); it != floater; ++ it) + if (SquareLengthType dist_sq = (point_getter(*it) - a).template cast().squaredNorm(); + dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest = it; + } + } else { + // Find Find the furthest point from the line . + const double dl2 = double(l2); + const Vec2d dv = v.template cast(); + for (auto it = std::next(anchor); it != floater; ++ it) { + const auto p = point_getter(*it); + const Vector va = (p - a).template cast(); + const SquareLengthType t = va.dot(v); + SquareLengthType dist_sq; + if (t <= 0) { + dist_sq = va.squaredNorm(); + } else if (t >= l2) { + dist_sq = (p - f).template cast().squaredNorm(); + } else if (double dt = double(t) / dl2; dt <= 0) { + dist_sq = va.squaredNorm(); + } else if (dt >= 1.) { + dist_sq = (p - f).template cast().squaredNorm(); + } else { + const Vector w = (dt * dv).cast(); + dist_sq = (w - va).squaredNorm(); + } + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest = it; + } + } + } + // remove point if less than tolerance + take_floater = max_dist_sq <= tolerance_sq; + } + if (take_floater) { + // The points between anchor and floater are close to the line. + // Drop the points between them. + a = f; + *out ++ = std::move(*floater); + anchor = floater; + assert(dpStack.back() == floater); + dpStack.pop_back(); + if (dpStack.empty()) + break; + floater = dpStack.back(); + f = point_getter(*floater); + } else { + // The furthest point is too far from the segment . + // Divide recursively. + floater = furthest; + f = point_getter(*floater); + dpStack.emplace_back(floater); + } + } + } + } + return out; +} + +// Reduces polyline in the +inline OutputIterator douglas_peucker(Points::const_iterator begin, Points::const_iterator end, OutputIterator out, const double tolerance) +{ + return douglas_peucker(begin, end, out, tolerance, [](const Point &p) { return p; }); +} + +inline Points douglas_peucker(const Points &src, const double tolerance) +{ + Points out; + out.reserve(src.size()); + douglas_peucker(src.begin(), src.end(), std::back_inserter(out), tolerance); + return out; +} + class MultiPoint { public: @@ -86,8 +203,8 @@ public: } } - static Points douglas_peucker(const Points &points, const double tolerance); - static Points visivalingam(const Points& pts, const double& tolerance); + static Points douglas_peucker(const Points &src, const double tolerance) { return Slic3r::douglas_peucker(src, tolerance); } + static Points visivalingam(const Points &src, const double tolerance); inline auto begin() { return points.begin(); } inline auto begin() const { return points.begin(); } @@ -124,16 +241,20 @@ extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents_rotated(const Points &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); -inline double length(const Points &pts) { +inline double length(const Points::const_iterator begin, const Points::const_iterator end) { double total = 0; - if (! pts.empty()) { - auto it = pts.begin(); - for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev) + if (begin != end) { + auto it = begin; + for (auto it_prev = it ++; it != end; ++ it, ++ it_prev) total += (*it - *it_prev).cast().norm(); } return total; } +inline double length(const Points &pts) { + return length(pts.begin(), pts.end()); +} + inline double area(const Points &polygon) { double area = 0.; for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index e5381cc2c5..9f9647b37f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -126,20 +126,18 @@ ExtrusionMultiPath PerimeterGenerator::thick_polyline_to_multi_path(const ThickP const double w = fmax(line.a_width, line.b_width); const Flow new_flow = (role.is_bridge() && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); - if (path.polyline.points.empty()) { - path.polyline.append(line.a); - path.polyline.append(line.b); + if (path.empty()) { // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. + path = { ExtrusionAttributes{ path.role(), new_flow } }; + path.polyline.append(line.a); + path.polyline.append(line.b); #ifdef SLIC3R_DEBUG printf(" filling %f gap\n", flow.width); #endif - path.mm3_per_mm = new_flow.mm3_per_mm(); - path.width = new_flow.width(); - path.height = new_flow.height(); } else { - assert(path.width >= EPSILON); - thickness_delta = scaled(fabs(path.width - new_flow.width())); + assert(path.width() >= EPSILON); + thickness_delta = scaled(fabs(path.width() - new_flow.width())); if (thickness_delta <= merge_tolerance) { // the width difference between this line and the current flow // (of the previous line) width is within the accepted tolerance @@ -332,10 +330,12 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, intersection_pl({ polygon }, lower_slices_polygons_clipped), - role_normal, - is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, - is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), - float(params.layer_height)); + ExtrusionAttributes{ + role_normal, + ExtrusionFlow{ is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, + is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), + float(params.layer_height) + } }); // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between @@ -343,23 +343,26 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, diff_pl({ polygon }, lower_slices_polygons_clipped), - role_overhang, - params.mm3_per_mm_overhang, - params.overhang_flow.width(), - params.overhang_flow.height()); + ExtrusionAttributes{ + role_overhang, + ExtrusionFlow{ params.mm3_per_mm_overhang, params.overhang_flow.width(), params.overhang_flow.height() } + }); // Reapply the nearest point search for starting point. // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); } else { - ExtrusionPath path(role_normal); - path.polyline = polygon.split_at_first_point(); - path.mm3_per_mm = is_external ? params.ext_mm3_per_mm : params.mm3_per_mm; - path.width = is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(); - path.height = float(params.layer_height); - paths.push_back(path); + paths.emplace_back(polygon.split_at_first_point(), + ExtrusionAttributes{ + role_normal, + ExtrusionFlow{ + is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, + is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), + float(params.layer_height) + } + }); } - + coll.append(ExtrusionLoop(std::move(paths), loop_role)); } @@ -390,11 +393,13 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; if (loop.is_contour) { - eloop->make_counter_clockwise(); + if (eloop->is_clockwise()) + eloop->reverse_loop(); out.append(std::move(children.entities)); out.entities.emplace_back(eloop); } else { - eloop->make_clockwise(); + if (eloop->is_counter_clockwise()) + eloop->reverse_loop(); out.entities.emplace_back(eloop); out.append(std::move(children.entities)); } @@ -610,10 +615,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P if (extrusion->is_closed) { ExtrusionLoop extrusion_loop(std::move(paths)); // Restore the orientation of the extrusion loop. - if (pg_extrusion.is_contour) - extrusion_loop.make_counter_clockwise(); - else - extrusion_loop.make_clockwise(); + if (pg_extrusion.is_contour == extrusion_loop.is_clockwise()) + extrusion_loop.reverse_loop(); for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) { assert(it->polyline.points.size() >= 2); @@ -967,11 +970,9 @@ std::tuple, Polygons> generate_extra_perimeters_over if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width Polygons shrinked = intersection(offset(prev, -0.3 * overhang_flow.scaled_spacing()), expanded_overhang_to_cover); - if (!shrinked.empty()) { + if (!shrinked.empty()) extrusion_paths_append(overhang_region, reconnect_polylines(perimeter, overhang_flow.scaled_spacing()), - ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), - overhang_flow.height()); - } + ExtrusionAttributes{ ExtrusionRole::OverhangPerimeter, overhang_flow }); Polylines fills; ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) : to_expolygons(shrinked); @@ -982,14 +983,12 @@ std::tuple, Polygons> generate_extra_perimeters_over if (!fills.empty()) { fills = intersection_pl(fills, shrinked_overhang_to_cover); extrusion_paths_append(overhang_region, reconnect_polylines(fills, overhang_flow.scaled_spacing()), - ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), - overhang_flow.height()); + ExtrusionAttributes{ ExtrusionRole::OverhangPerimeter, overhang_flow }); } break; } else { extrusion_paths_append(overhang_region, reconnect_polylines(perimeter, overhang_flow.scaled_spacing()), - ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), - overhang_flow.height()); + ExtrusionAttributes{ExtrusionRole::OverhangPerimeter, overhang_flow }); } if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; } diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 635d33368f..9eede0d740 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1651,6 +1651,7 @@ namespace client // Check whether the table X values are sorted. double x = expr_x.as_d(); + assert(! std::isnan(x)); bool evaluated = false; for (size_t i = 1; i < table.table.size(); ++i) { double x0 = table.table[i - 1].x; @@ -2106,7 +2107,7 @@ namespace client multiplicative_expression.name("multiplicative_expression"); assignment_statement = - variable_reference(_r1)[_a = _1] >> '=' > + (variable_reference(_r1)[_a = _1] >> '=') > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. initializer_list(_r1)[px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 2b49e81e26..f78b47e8b2 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -59,14 +59,12 @@ Pointf3s transform(const Pointf3s& points, const Transform3d& t) void Point::rotate(double angle, const Point ¢er) { - double cur_x = (double)(*this)(0); - double cur_y = (double)(*this)(1); - double s = ::sin(angle); - double c = ::cos(angle); - double dx = cur_x - (double)center(0); - double dy = cur_y - (double)center(1); - (*this)(0) = (coord_t)round( (double)center(0) + c * dx - s * dy ); - (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); + Vec2d cur = this->cast(); + double s = ::sin(angle); + double c = ::cos(angle); + auto d = cur - center.cast(); + this->x() = fast_round_up(center.x() + c * d.x() - s * d.y()); + this->y() = fast_round_up(center.y() + s * d.x() + c * d.y()); } bool has_duplicate_points(Points &&pts) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 9ac082924f..085075a611 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -192,11 +192,12 @@ public: Point(const Point &rhs) { *this = rhs; } explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {} // This constructor allows you to construct Point from Eigen expressions + // This constructor has to be implicit (non-explicit) to allow implicit conversion from Eigen expressions. template Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); } - static Point new_scale(const Vec2d &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); } - static Point new_scale(const Vec2f &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); } + template + static Point new_scale(const Eigen::MatrixBase &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); } // This method allows you to assign Eigen expressions to MyVectorType template diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 3f3fa24a3c..3a5c91cdc9 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -158,8 +158,10 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const } if (this->points.front() == point) { + //FIXME why is p1 NOT empty as in the case above? *p1 = { point }; *p2 = *this; + return; } auto min_dist2 = std::numeric_limits::max(); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a75517f378..0ff2f103c3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -44,6 +44,7 @@ #include "libslic3r.h" #include "Utils.hpp" #include "PlaceholderParser.hpp" +#include "GCode/Thumbnails.hpp" using boost::property_tree::ptree; @@ -459,12 +460,13 @@ static std::vector s_Preset_print_options { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", + "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "gcode_binary", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", + "elefant_foot_compensation", "xy_size_compensation", "resolution", "gcode_resolution", "arc_fitting", "arc_fitting_tolerance", + "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", @@ -1298,7 +1300,6 @@ static const std::set independent_from_extruder_number_options = { "filament_ramming_parameters", "gcode_substitutions", "post_process", - "thumbnails", }; bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key) @@ -1322,6 +1323,15 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi } else if (opt_key == "default_filament_profile") { // Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help. // Also the length of this field may differ, which may lead to a crash if the block below is used. + } else if (opt_key == "thumbnails") { + // "thumbnails" can not containes a extentions in old config but are valid and use PNG extention by default + // So, check if "thumbnails" is really changed + // We will compare full thumnails instead of exactly config values + auto [thumbnails, er] = GCodeThumbnails::make_and_check_thumbnail_list(config_this); + auto [thumbnails_new, er_new] = GCodeThumbnails::make_and_check_thumbnail_list(config_other); + if (thumbnails != thumbnails_new || er != er_new) + // if those strings are actually the same, erase them from the list of dirty oprions + diff.emplace_back(opt_key); } else { switch (other_opt->type()) { case coInts: add_correct_opts_to_diff(opt_key, diff, config_other, config_this); break; @@ -1345,7 +1355,14 @@ bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference) { if (edited != nullptr && reference != nullptr) { // Only compares options existing in both configs. - if (! reference->config.equals(edited->config)) + bool is_dirty = !reference->config.equals(edited->config); + if (is_dirty && edited->type != Preset::TYPE_FILAMENT) { + // for non-filaments preset check deep difference for compared configs + // there can be cases (as for thumbnails), when configs can logically equal + // even when their values are not equal. + is_dirty = !deep_diff(edited->config, reference->config).empty(); + } + if (is_dirty) return true; // The "compatible_printers" option key is handled differently from the others: // It is not mandatory. If the key is missing, it means it is compatible with any printer. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 606efd0b7c..a9dc23d2c2 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -28,6 +28,7 @@ #include #include +#include // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions. @@ -883,14 +884,22 @@ DynamicPrintConfig PresetBundle::full_sla_config() const // If the file is loaded successfully, its print / filament / printer profiles will be activated. ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, ForwardCompatibilitySubstitutionRule compatibility_rule) { - if (is_gcode_file(path)) { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, compatibility_rule); + if (is_gcode_file(path)) { + FILE* file = boost::nowide::fopen(path.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError(format("Error opening file %1%", path)); + std::vector cs_buffer(65536); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; + fclose(file); + + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + ConfigSubstitutions config_substitutions = is_binary ? config.load_from_binary_gcode_file(path, compatibility_rule) : + config.load_from_gcode_file(path, compatibility_rule); Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return config_substitutions; - } + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } // 1) Try to load the config file into a boost property tree. boost::property_tree::ptree tree; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8ac58aad8d..cbc9404e84 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -139,6 +139,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "perimeter_acceleration", "post_process", "gcode_substitutions", + "gcode_binary", "printer_notes", "retract_before_travel", "retract_before_wipe", @@ -954,8 +955,10 @@ void Print::process() tbb::parallel_for(tbb::blocked_range(0, m_objects.size(), 1), [this](const tbb::blocked_range &range) { for (size_t idx = range.begin(); idx < range.end(); ++idx) { - m_objects[idx]->generate_support_material(); - m_objects[idx]->estimate_curled_extrusions(); + PrintObject &obj = *m_objects[idx]; + obj.generate_support_material(); + obj.estimate_curled_extrusions(); + obj.calculate_overhanging_perimeters(); } }, tbb::simple_partitioner()); @@ -1042,7 +1045,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor this->set_status(90, message); // Create GCode on heap, it has quite a lot of data. - std::unique_ptr gcode(new GCode); + std::unique_ptr gcode(new GCodeGenerator); gcode->do_export(this, path.c_str(), result, thumbnail_cb); if (m_conflict_result.has_value()) @@ -1158,13 +1161,15 @@ void Print::_make_skirt() } // Extrude the skirt loop. ExtrusionLoop eloop(elrSkirt); - eloop.paths.emplace_back(ExtrusionPath( - ExtrusionPath( + eloop.paths.emplace_back( + ExtrusionAttributes{ ExtrusionRole::Skirt, - (float)mm3_per_mm, // this will be overridden at G-code export time - flow.width(), - (float)first_layer_height // this will be overridden at G-code export time - ))); + ExtrusionFlow{ + float(mm3_per_mm), // this will be overridden at G-code export time + flow.width(), + float(first_layer_height) // this will be overridden at G-code export time + } + }); eloop.paths.back().polyline = loop.split_at_first_point(); m_skirt.append(eloop); if (m_config.min_skirt_length.value > 0) { @@ -1590,6 +1595,26 @@ std::string Print::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } +const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; +const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] ="; + +const std::string PrintStatistics::TotalFilamentUsedG = "total filament used [g]"; +const std::string PrintStatistics::TotalFilamentUsedGMask = "; total filament used [g] ="; +const std::string PrintStatistics::TotalFilamentUsedGValueMask = "; total filament used [g] = %.2lf\n"; + +const std::string PrintStatistics::FilamentUsedCm3 = "filament used [cm3]"; +const std::string PrintStatistics::FilamentUsedCm3Mask = "; filament used [cm3] ="; + +const std::string PrintStatistics::FilamentUsedMm = "filament used [mm]"; +const std::string PrintStatistics::FilamentUsedMmMask = "; filament used [mm] ="; + +const std::string PrintStatistics::FilamentCost = "filament cost"; +const std::string PrintStatistics::FilamentCostMask = "; filament cost ="; + +const std::string PrintStatistics::TotalFilamentCost = "total filament cost"; +const std::string PrintStatistics::TotalFilamentCostMask = "; total filament cost ="; +const std::string PrintStatistics::TotalFilamentCostValueMask = "; total filament cost = %.2lf\n"; + DynamicConfig PrintStatistics::config() const { DynamicConfig config; @@ -1670,8 +1695,8 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co } - ExtrusionPath path(ExtrusionRole::WipeTower, 0.0, 0.0, lh); - path.polyline = { minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }; + ExtrusionPath path({ minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }, + ExtrusionAttributes{ ExtrusionRole::WipeTower, ExtrusionFlow{ 0.0, 0.0, lh } }); paths.push_back({ path }); // We added the border, now add several parallel lines so we can detect an object that is fully inside the tower. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 8c6353b4a0..8e55044531 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -49,7 +49,7 @@ namespace Slic3r { -class GCode; +class GCodeGenerator; class Layer; class ModelObject; class Print; @@ -87,7 +87,7 @@ enum PrintStep : unsigned int { enum PrintObjectStep : unsigned int { posSlice, posPerimeters, posPrepareInfill, - posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posEstimateCurledExtrusions, posCount, + posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posEstimateCurledExtrusions, posCalculateOverhangingPerimeters, posCount, }; // A PrintRegion object represents a group of volumes to print @@ -396,6 +396,7 @@ private: void generate_support_spots(); void generate_support_material(); void estimate_curled_extrusions(); + void calculate_overhanging_perimeters(); void slice_volumes(); // Has any support (not counting the raft). @@ -548,6 +549,21 @@ struct PrintStatistics filament_stats.clear(); printing_extruders.clear(); } + + static const std::string FilamentUsedG; + static const std::string FilamentUsedGMask; + static const std::string TotalFilamentUsedG; + static const std::string TotalFilamentUsedGMask; + static const std::string TotalFilamentUsedGValueMask; + static const std::string FilamentUsedCm3; + static const std::string FilamentUsedCm3Mask; + static const std::string FilamentUsedMm; + static const std::string FilamentUsedMmMask; + static const std::string FilamentCost; + static const std::string FilamentCostMask; + static const std::string TotalFilamentCost; + static const std::string TotalFilamentCostMask; + static const std::string TotalFilamentCostValueMask; }; using PrintObjectPtrs = std::vector; @@ -717,7 +733,7 @@ private: Polygons m_sequential_print_clearance_contours; // To allow GCode to set the Print's GCodeExport step status. - friend class GCode; + friend class GCodeGenerator; // To allow GCodeProcessor to emit warnings. friend class GCodeProcessor; // Allow PrintObject to access m_mutex and m_cancel_callback. diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 63d1a27067..18d9833974 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -21,7 +21,7 @@ void PrintTryCancel::operator()() const size_t PrintStateBase::g_last_timestamp = 0; // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. -void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const +void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string & /* default_output_ext */) const { // get the first input file name std::string input_file; @@ -54,7 +54,7 @@ void PrintBase::update_object_placeholders(DynamicConfig &config, const std::str // get basename with and without suffix const std::string input_filename = boost::filesystem::path(input_file).filename().string(); const std::string input_filename_base = input_filename.substr(0, input_filename.find_last_of(".")); - config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_ext)); +// config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_output_ext)); config.set_key_value("input_filename_base", new ConfigOptionString(input_filename_base)); } } @@ -70,7 +70,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str PlaceholderParser::update_timestamp(cfg); this->update_object_placeholders(cfg, default_ext); if (! filename_base.empty()) { - cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext)); +// cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext)); cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); } try { diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 779c7674ab..4b4976ea43 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -552,7 +552,7 @@ protected: // To be called by this->output_filename() with the format string pulled from the configuration layer. std::string output_filename(const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override = nullptr) const; // Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects. - void update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const; + void update_object_placeholders(DynamicConfig &config, const std::string &default_output_ext) const; Model m_model; DynamicPrintConfig m_full_print_config; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0d925d729f..23e17e0018 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -24,6 +24,7 @@ #include "I18N.hpp" #include "SLA/SupportTree.hpp" +#include "GCode/Thumbnails.hpp" #include #include @@ -55,6 +56,13 @@ static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values & template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values() { return s_keys_map_##NAME; } \ template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names() { return s_keys_names_##NAME; } +static const t_config_enum_values s_keys_map_ArcFittingType { + { "disabled", int(ArcFittingType::Disabled) }, + { "emit_center", int(ArcFittingType::EmitCenter) }, + { "emit_radius", int(ArcFittingType::EmitRadius) } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ArcFittingType) + static t_config_enum_values s_keys_map_PrinterTechnology { { "FFF", ptFFF }, { "SLA", ptSLA } @@ -299,12 +307,12 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.)); - def = this->add("thumbnails", coPoints); + def = this->add("thumbnails", coString); def->label = L("G-code thumbnails"); - def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\""); + def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxYxEXT, XxYxEXT, ...\""); def->mode = comExpert; def->gui_type = ConfigOptionDef::GUIType::one_string; - def->set_default_value(new ConfigOptionPoints()); + def->set_default_value(new ConfigOptionString()); def = this->add("thumbnails_format", coEnum); def->label = L("Format of G-code thumbnails"); @@ -418,6 +426,27 @@ void PrintConfigDef::init_fff_params() { ConfigOptionDef* def; + def = this->add("arc_fitting", coEnum); + def->label = L("Arc fitting"); + def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. " + "And the fitting tolerance is same with resolution"); + def->set_enum({ + { "disabled", "Disabled" }, + { "emit_center", "Enabled: G2/3 I J" }, + { "emit_radius", "Enabled: G2/3 R" } + }); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(ArcFittingType::Disabled)); + + def = this->add("arc_fitting_tolerance", coFloatOrPercent); + def->label = L("Arc fitting tolerance"); + def->sidetext = L("mm or %"); + def->tooltip = L("When using the arc_fitting option, allow the curve to deviate a cetain % from the collection of strait paths.\n" + "Can be a mm value or a percentage of the current extrusion width."); + def->mode = comAdvanced; + def->min = 0; + def->set_default_value(new ConfigOptionFloatOrPercent(5, true)); + // Maximum extruder temperature, bumped to 1500 to support printing of glass. const int max_temp = 1500; def = this->add("avoid_crossing_curled_overhangs", coBool); @@ -1476,6 +1505,12 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionStrings()); + def = this->add("gcode_binary", coBool); + def->label = L("Export as binary G-code"); + def->tooltip = L("Exports G-code in binary format."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(0)); + def = this->add("high_current_on_filament_swap", coBool); def->label = L("High extruder current on filament swap"); def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange" @@ -3061,18 +3096,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); - def = this->add("threads", coInt); - def->label = L("Threads"); - def->tooltip = L("Threads are used to parallelize long-running tasks. Optimal threads number " - "is slightly above the number of available cores/processors."); - def->readonly = true; - def->min = 1; - { - int threads = (unsigned int)boost::thread::hardware_concurrency(); - def->set_default_value(new ConfigOptionInt(threads > 0 ? threads : 2)); - def->cli = ConfigOptionDef::nocli; - } - def = this->add("toolchange_gcode", coString); def->label = L("Tool change G-code"); def->tooltip = L("This custom code is inserted before every toolchange. Placeholder variables for all PrusaSlicer settings " @@ -4349,6 +4372,35 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va // Don't convert single options here, implement such conversion in PrintConfigDef::handle_legacy() instead. void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) { + if (config.has("thumbnails")) { + std::string extention; + if (config.has("thumbnails_format")) { + if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) { + auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt()); + if (label.has_value()) + extention = *label; + } + } + + std::string thumbnails_str = config.opt_string("thumbnails"); + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, extention); + + if (errors != enum_bitmask()) { + std::string error_str = "\n" + format("Invalid value provided for parameter %1%: %2%", "thumbnails", thumbnails_str); + error_str += GCodeThumbnails::get_error_string(errors); + throw BadOptionValueException(error_str); + } + + if (!thumbnails_list.empty()) { + const auto& extentions = ConfigOptionEnum::get_enum_names(); + thumbnails_str.clear(); + for (const auto& [ext, size] : thumbnails_list) + thumbnails_str += format("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(ext)]); + thumbnails_str.resize(thumbnails_str.length() - 2); + + config.set_key_value("thumbnails", new ConfigOptionString(thumbnails_str)); + } + } } const PrintConfigDef print_config_def; @@ -4991,6 +5043,309 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } } +// SlicingStatesConfigDefs + +ReadOnlySlicingStatesConfigDef::ReadOnlySlicingStatesConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("zhop", coFloat); + def->label = L("Current z-hop"); + def->tooltip = L("Contains z-hop present at the beginning of the custom G-code block."); +} + +ReadWriteSlicingStatesConfigDef::ReadWriteSlicingStatesConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("position", coFloats); + def->label = L("Position"); + def->tooltip = L("Position of the extruder at the beginning of the custom G-code block. If the custom G-code travels somewhere else, " + "it should write to this variable so PrusaSlicer knows where it travels from when it gets control back."); + + def = this->add("e_retracted", coFloats); + def->label = L("Retraction"); + def->tooltip = L("Retraction state at the beginning of the custom G-code block. If the custom G-code moves the extruder axis, " + "it should write to this variable so PrusaSlicer deretracts correctly when it gets control back."); + + def = this->add("e_restart_extra", coFloats); + def->label = L("Extra deretraction"); + def->tooltip = L("Currently planned extra extruder priming after deretraction."); + + def = this->add("e_position", coFloats); + def->label = L("Absolute E position"); + def->tooltip = L("Current position of the extruder axis. Only used with absolute extruder addressing."); +} + +OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("current_extruder", coInt); + def->label = L("Current extruder"); + def->tooltip = L("Zero-based index of currently used extruder."); + + def = this->add("current_object_idx", coInt); + def->label = L("Current object index"); + def->tooltip = L("Specific for sequential printing. Zero-based index of currently printed object."); + + def = this->add("has_single_extruder_multi_material_priming", coBool); + def->label = L("Has single extruder MM priming"); + def->tooltip = L("Are the extra multi-material priming regions used in this print?"); + + def = this->add("has_wipe_tower", coBool); + def->label = L("Has wipe tower"); + def->tooltip = L("Whether or not wipe tower is being generated in the print."); + + def = this->add("initial_extruder", coInt); + def->label = L("Initial extruder"); + def->tooltip = L("Zero-based index of the first extruder used in the print. Same as initial_tool."); + + def = this->add("initial_filament_type", coString); + def->label = L("Initial filament type"); + def->tooltip = L("String containing filament type of the first used extruder."); + + def = this->add("initial_tool", coInt); + def->label = L("Initial tool"); + def->tooltip = L("Zero-based index of the first extruder used in the print. Same as initial_extruder."); + + def = this->add("is_extruder_used", coBools); + def->label = L("Is extruder used?"); + def->tooltip = L("Vector of bools stating whether a given extruder is used in the print."); +} + +PrintStatisticsConfigDef::PrintStatisticsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("extruded_volume", coFloats); + def->label = L("Volume per extruder"); + def->tooltip = L("Total filament volume extruded per extruder during the entire print."); + + def = this->add("normal_print_time", coString); + def->label = L("Print time (normal mode)"); + def->tooltip = L("Estimated print time when printed in normal mode (i.e. not in silent mode). Same as print_time."); + + def = this->add("num_printing_extruders", coInt); + def->label = L("Number of printing extruders"); + def->tooltip = L("Number of extruders used during the print."); + + def = this->add("print_time", coString); + def->label = L("Print time (normal mode)"); + def->tooltip = L("Estimated print time when printed in normal mode (i.e. not in silent mode). Same as normal_print_time."); + + def = this->add("printing_filament_types", coString); + def->label = L("Used filament types"); + def->tooltip = L("Comma-separated list of all filament types used during the print."); + + def = this->add("silent_print_time", coString); + def->label = L("Print time (silent mode)"); + def->tooltip = L("Estimated print time when printed in silent mode."); + + def = this->add("total_cost", coFloat); + def->label = L("Total cost"); + def->tooltip = L("Total cost of all material used in the print. Calculated from filament_cost value in Filament Settings."); + + def = this->add("total_weight", coFloat); + def->label = L("Total weight"); + def->tooltip = L("Total weight of the print. Calculated from filament_density value in Filament Settings."); + + def = this->add("total_wipe_tower_cost", coFloat); + def->label = L("Total wipe tower cost"); + def->tooltip = L("Total cost of the material wasted on the wipe tower. Calculated from filament_cost value in Filament Settings."); + + def = this->add("total_wipe_tower_filament", coFloat); + def->label = L("Wipe tower volume"); + def->tooltip = L("Total filament volume extruded on the wipe tower."); + + def = this->add("used_filament", coFloat); + def->label = L("Used filament"); + def->tooltip = L("Total length of filament used in the print."); + + def = this->add("total_toolchanges", coInt); + def->label = L("Total toolchanges"); + def->tooltip = L("Number of toolchanges during the print."); + + def = this->add("extruded_volume_total", coFloat); + def->label = L("Total volume"); + def->tooltip = L("Total volume of filament used during the entire print."); + + def = this->add("extruded_weight", coFloats); + def->label = L("Weight per extruder"); + def->tooltip = L("Weight per extruder extruded during the entire print. Calculated from filament_density value in Filament Settings."); + + def = this->add("extruded_weight_total", coFloat); + def->label = L("Total weight"); + def->tooltip = L("Total weight of the print. Calculated from filament_density value in Filament Settings."); + + def = this->add("total_layer_count", coInt); + def->label = L("Total layer count"); + def->tooltip = L("Number of layers in the entire print."); +} + +ObjectsInfoConfigDef::ObjectsInfoConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("num_objects", coInt); + def->label = L("Number of objects"); + def->tooltip = L("Total number of objects in the print."); + + def = this->add("num_instances", coInt); + def->label = L("Number of instances"); + def->tooltip = L("Total number of object instances in the print, summed over all objects."); + + def = this->add("scale", coStrings); + def->label = L("Scale per object"); + def->tooltip = L("Contains a string with the information about what scaling was applied to the individual objects. " + "Indexing of the objects is zero-based (first object has index 0).\n" + "Example: 'x:100% y:50% z:100'."); + + def = this->add("input_filename_base", coString); + def->label = L("Input filename without extension"); + def->tooltip = L("Source filename of the first object, without extension."); +} + +DimensionsConfigDef::DimensionsConfigDef() +{ + ConfigOptionDef* def; + + const std::string point_tooltip = L("The vector has two elements: x and y coordinate of the point. Values in mm."); + const std::string bb_size_tooltip = L("The vector has two elements: x and y dimension of the bounding box. Values in mm."); + + def = this->add("first_layer_print_convex_hull", coPoints); + def->label = L("First layer convex hull"); + def->tooltip = L("Vector of points of the first layer convex hull. Each element has the following format: " + "'[x, y]' (x and y are floating-point numbers in mm)."); + + def = this->add("first_layer_print_min", coFloats); + def->label = L("Bottom-left corner of first layer bounding box"); + def->tooltip = point_tooltip; + + def = this->add("first_layer_print_max", coFloats); + def->label = L("Top-right corner of first layer bounding box"); + def->tooltip = point_tooltip; + + def = this->add("first_layer_print_size", coFloats); + def->label = L("Size of the first layer bounding box"); + def->tooltip = bb_size_tooltip; + + def = this->add("print_bed_min", coFloats); + def->label = L("Bottom-left corner of print bed bounding box"); + def->tooltip = point_tooltip; + + def = this->add("print_bed_max", coFloats); + def->label = L("Top-right corner of print bed bounding box"); + def->tooltip = point_tooltip; + + def = this->add("print_bed_size", coFloats); + def->label = L("Size of the print bed bounding box"); + def->tooltip = bb_size_tooltip; +} + +TimestampsConfigDef::TimestampsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("timestamp", coString); + def->label = L("Timestamp"); + def->tooltip = L("String containing current time in yyyyMMdd-hhmmss format."); + + def = this->add("year", coInt); + def->label = L("Year"); + + def = this->add("month", coInt); + def->label = L("Month"); + + def = this->add("day", coInt); + def->label = L("Day"); + + def = this->add("hour", coInt); + def->label = L("Hour"); + + def = this->add("minute", coInt); + def->label = L("Minute"); + + def = this->add("second", coInt); + def->label = L("Second"); +} + +OtherPresetsConfigDef::OtherPresetsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("num_extruders", coInt); + def->label = L("Number of extruders"); + def->tooltip = L("Total number of extruders, regardless of whether they are used in the current print."); + + def = this->add("print_preset", coString); + def->label = L("Print preset name"); + def->tooltip = L("Name of the print preset used for slicing."); + + def = this->add("filament_preset", coStrings); + def->label = L("Filament preset name"); + def->tooltip = L("Names of the filament presets used for slicing. The variable is a vector " + "containing one name for each extruder."); + + def = this->add("printer_preset", coString); + def->label = L("Printer preset name"); + def->tooltip = L("Name of the printer preset used for slicing."); + + def = this->add("physical_printer_preset", coString); + def->label = L("Physical printer name"); + def->tooltip = L("Name of the physical printer used for slicing."); +} + + +static std::map s_CustomGcodeSpecificPlaceholders{ + {"start_filament_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, + {"end_filament_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, + {"end_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, + {"before_layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, + {"layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, + {"toolchange_gcode", {"layer_num", "layer_z", "max_layer_z", "previous_extruder", "next_extruder", "toolchange_z"}}, +}; + +const std::map& custom_gcode_specific_placeholders() +{ + return s_CustomGcodeSpecificPlaceholders; +} + +CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("layer_num", coInt); + def->label = L("Layer number"); + def->tooltip = L("Zero-based index of the current layer (i.e. first layer is number 0)."); + + def = this->add("layer_z", coFloat); + def->label = L("Layer z"); + def->tooltip = L("Height of the current layer above the print bed, measured to the top of the layer."); + + def = this->add("max_layer_z", coFloat); + def->label = L("Maximal layer z"); + def->tooltip = L("Height of the last layer above the print bed."); + + def = this->add("filament_extruder_id", coInt); + def->label = L("Current extruder index"); + def->tooltip = L("Zero-based index of currently used extruder (i.e. first extruder has index 0)."); + + def = this->add("previous_extruder", coInt); + def->label = L("Previous extruder"); + def->tooltip = L("Index of the extruder that is being unloaded. The index is zero based (first extruder has index 0)."); + + def = this->add("next_extruder", coInt); + def->label = L("Next extruder"); + def->tooltip = L("Index of the extruder that is being loaded. The index is zero based (first extruder has index 0)."); + + def = this->add("toolchange_z", coFloat); + def->label = L("Toolchange z"); + def->tooltip = L("Height above the print bed when the toolchange takes place. Usually the same as layer_z, but can be different."); +} + +const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; + uint64_t ModelConfig::s_last_timestamp = 1; static Points to_points(const std::vector &dpts) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index db6a538126..390f91daf6 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -47,6 +47,12 @@ namespace Slic3r { +enum class ArcFittingType { + Disabled, + EmitCenter, + EmitRadius, +}; + enum GCodeFlavor : unsigned char { gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfKlipper, gcfSailfish, gcfMach3, gcfMachinekit, gcfSmoothie, gcfNoExtrusion, @@ -158,6 +164,7 @@ enum class GCodeThumbnailsFormat { template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names(); \ template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values(); +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ArcFittingType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(MachineLimitsUsage) @@ -688,6 +695,8 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( GCodeConfig, + ((ConfigOptionEnum, arc_fitting)) + ((ConfigOptionFloatOrPercent, arc_fitting_tolerance)) ((ConfigOptionBool, autoemit_temperature_commands)) ((ConfigOptionString, before_layer_gcode)) ((ConfigOptionString, between_objects_gcode)) @@ -727,6 +736,7 @@ PRINT_CONFIG_CLASS_DEFINE( // i - case insensitive // w - whole word ((ConfigOptionStrings, gcode_substitutions)) + ((ConfigOptionBool, gcode_binary)) ((ConfigOptionString, layer_gcode)) ((ConfigOptionFloat, max_print_speed)) ((ConfigOptionFloat, max_volumetric_speed)) @@ -843,7 +853,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInt, standby_temperature_delta)) ((ConfigOptionInts, temperature)) ((ConfigOptionInt, threads)) - ((ConfigOptionPoints, thumbnails)) + ((ConfigOptionString, thumbnails)) ((ConfigOptionEnum, thumbnails_format)) ((ConfigOptionFloat, top_solid_infill_acceleration)) ((ConfigOptionFloat, travel_acceleration)) @@ -1180,6 +1190,68 @@ public: CLIMiscConfigDef(); }; +typedef std::string t_custom_gcode_key; +// This map containes list of specific placeholders for each custom G-code, if any exist +const std::map& custom_gcode_specific_placeholders(); + +// Next classes define placeholders used by GUI::EditGCodeDialog. + +class ReadOnlySlicingStatesConfigDef : public ConfigDef +{ +public: + ReadOnlySlicingStatesConfigDef(); +}; + +class ReadWriteSlicingStatesConfigDef : public ConfigDef +{ +public: + ReadWriteSlicingStatesConfigDef(); +}; + +class OtherSlicingStatesConfigDef : public ConfigDef +{ +public: + OtherSlicingStatesConfigDef(); +}; + +class PrintStatisticsConfigDef : public ConfigDef +{ +public: + PrintStatisticsConfigDef(); +}; + +class ObjectsInfoConfigDef : public ConfigDef +{ +public: + ObjectsInfoConfigDef(); +}; + +class DimensionsConfigDef : public ConfigDef +{ +public: + DimensionsConfigDef(); +}; + +class TimestampsConfigDef : public ConfigDef +{ +public: + TimestampsConfigDef(); +}; + +class OtherPresetsConfigDef : public ConfigDef +{ +public: + OtherPresetsConfigDef(); +}; + +// This classes defines all custom G-code specific placeholders. +class CustomGcodeSpecificConfigDef : public ConfigDef +{ +public: + CustomGcodeSpecificConfigDef(); +}; +extern const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; + // This class defines the command line options representing actions. extern const CLIActionsConfigDef cli_actions_config_def; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1643fae838..228cc00672 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -12,7 +12,9 @@ #include "ExPolygon.hpp" #include "Exception.hpp" #include "Flow.hpp" +#include "GCode/ExtrusionProcessor.hpp" #include "KDTreeIndirect.hpp" +#include "Line.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "Polyline.hpp" @@ -542,6 +544,65 @@ void PrintObject::estimate_curled_extrusions() } } +void PrintObject::calculate_overhanging_perimeters() +{ + if (this->set_started(posCalculateOverhangingPerimeters)) { + BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - start"; + m_print->set_status(89, _u8L("Calculating overhanging perimeters")); + std::vector extruders; + std::unordered_set regions_with_dynamic_speeds; + for (const PrintRegion *pr : this->print()->m_print_regions) { + if (pr->config().enable_dynamic_overhang_speeds.getBool()) { + regions_with_dynamic_speeds.insert(pr); + } + extruders.clear(); + pr->collect_object_printing_extruders(*this->print(), extruders); + auto cfg = this->print()->config(); + if (std::any_of(extruders.begin(), extruders.end(), + [&cfg](unsigned int extruder_id) { return cfg.enable_dynamic_fan_speeds.get_at(extruder_id); })) { + regions_with_dynamic_speeds.insert(pr); + } + } + + if (!regions_with_dynamic_speeds.empty()) { + std::unordered_map> curled_lines; + std::unordered_map> unscaled_polygons_lines; + for (const Layer *l : this->layers()) { + curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; + unscaled_polygons_lines[l->id()] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(l->lslices)}; + } + curled_lines[size_t(-1)] = {}; + unscaled_polygons_lines[size_t(-1)] = {}; + + tbb::parallel_for(tbb::blocked_range(0, m_layers.size()), [this, &curled_lines, &unscaled_polygons_lines, + ®ions_with_dynamic_speeds]( + const tbb::blocked_range &range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + auto l = m_layers[layer_idx]; + if (l->id() == 0) { // first layer, do not split + continue; + } + for (LayerRegion *layer_region : l->regions()) { + if (regions_with_dynamic_speeds.find(layer_region->m_region) == regions_with_dynamic_speeds.end()) { + continue; + } + size_t prev_layer_id = l->lower_layer ? l->lower_layer->id() : size_t(-1); + layer_region->m_perimeters = + ExtrusionProcessor::calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, + unscaled_polygons_lines[prev_layer_id], + curled_lines[l->id()]); + } + } + }); + + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - end"; + } + this->set_done(posCalculateOverhangingPerimeters); + } +} + std::pair PrintObject::prepare_adaptive_infill_data( const std::vector> &surfaces_w_bottom_z) const { @@ -653,7 +714,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "first_layer_extrusion_width" || opt_key == "perimeter_extrusion_width" || opt_key == "infill_overlap" - || opt_key == "external_perimeters_first") { + || opt_key == "external_perimeters_first" + || opt_key == "arc_fitting" + || opt_key == "arc_fitting_tolerance") { steps.emplace_back(posPerimeters); } else if ( opt_key == "gap_fill_enabled" @@ -854,7 +917,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, posEstimateCurledExtrusions }); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, posEstimateCurledExtrusions, posCalculateOverhangingPerimeters }); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_steps({ posInfill, posIroning, posSupportSpotsSearch}); @@ -863,7 +926,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step) invalidated |= m_print->invalidate_steps({ psSkirtBrim }); } else if (step == posSlice) { invalidated |= this->invalidate_steps({posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, - posSupportMaterial, posEstimateCurledExtrusions}); + posSupportMaterial, posEstimateCurledExtrusions, posCalculateOverhangingPerimeters}); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); m_slicing_params.valid = false; } else if (step == posSupportMaterial) { @@ -1557,7 +1620,7 @@ void PrintObject::discover_vertical_shells() to_polygons(m_layers[idx_layer + 1]->lslices) : Polygons{}; object_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); - internal_volume = closing(polygonsInternal, SCALED_EPSILON); + internal_volume = closing(polygonsInternal, float(SCALED_EPSILON)); } // The regularization operation may cause scattered tiny drops on the smooth parts of the model, filter them out diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 2b1e8370df..fcab5d62d9 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1010,16 +1010,20 @@ std::vector> chain_segments_greedy2(SegmentEndPointFunc return chain_segments_greedy_constrained_reversals2_(end_point_func, could_reverse_func, num_segments, start_near); } -std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) +std::vector> chain_extrusion_entities(const std::vector &entities, const Point *start_near, const bool reversed) { - auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; - auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; - std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); + auto segment_end_point = [&entities, reversed](size_t idx, bool first_point) -> const Point& { return first_point == reversed ? entities[idx]->last_point() : entities[idx]->first_point(); }; + auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; + std::vector> out = chain_segments_greedy_constrained_reversals( + segment_end_point, could_reverse, entities.size(), start_near); for (std::pair &segment : out) { ExtrusionEntity *ee = entities[segment.first]; if (ee->is_loop()) // Ignore reversals for loops, as the start point equals the end point. segment.second = false; + else if (reversed) + // Input was already reversed. + segment.second = ! segment.second; // Is can_reverse() respected by the reversals? assert(ee->can_reverse() || ! segment.second); } @@ -1045,6 +1049,33 @@ void chain_and_reorder_extrusion_entities(std::vector &entitie reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); } +ExtrusionEntityReferences chain_extrusion_references(const std::vector &entities, const Point *start_near, const bool reversed) +{ + const std::vector> chain = chain_extrusion_entities(entities, start_near, reversed); + ExtrusionEntityReferences out; + out.reserve(chain.size()); + for (const std::pair &idx : chain) { + assert(entities[idx.first] != nullptr); + out.push_back({ *entities[idx.first], idx.second }); + } + return out; +} + +ExtrusionEntityReferences chain_extrusion_references(const ExtrusionEntityCollection &eec, const Point *start_near, const bool reversed) +{ + if (eec.no_sort) { + ExtrusionEntityReferences out; + out.reserve(eec.entities.size()); + for (const ExtrusionEntity *ee : eec.entities) { + assert(ee != nullptr); + // Never reverse a loop. + out.push_back({ *ee, ! ee->is_loop() && reversed }); + } + return out; + } else + return chain_extrusion_references(eec.entities, start_near, reversed); +} + std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) { auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index dd48be1d34..243bb221ea 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -22,13 +22,25 @@ namespace Slic3r { class ExPolygon; using ExPolygons = std::vector; +// Used by chain_expolygons() std::vector chain_points(const Points &points, Point *start_near = nullptr); +// Used to give layer islands a print order. std::vector chain_expolygons(const ExPolygons &expolygons, Point *start_near = nullptr); -std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); +// Chain extrusion entities by a shortest distance. Returns the ordered extrusions together with a "reverse" flag. +// Set input "reversed" to true if the vector of "entities" is to be considered to be reversed once already. +std::vector> chain_extrusion_entities(const std::vector &entities, const Point *start_near = nullptr, const bool reversed = false); +// Reorder & reverse extrusion entities in place based on the "chain" ordering. void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain); +// Reorder & reverse extrusion entities in place. void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); +// Chain extrusion entities by a shortest distance. Returns the ordered extrusions together with a "reverse" flag. +// Set input "reversed" to true if the vector of "entities" is to be considered to be reversed. +ExtrusionEntityReferences chain_extrusion_references(const std::vector &entities, const Point *start_near = nullptr, const bool reversed = false); +// The same as above, respect eec.no_sort flag. +ExtrusionEntityReferences chain_extrusion_references(const ExtrusionEntityCollection &eec, const Point *start_near = nullptr, const bool reversed = false); + std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain); void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 3f08e2f960..64a6924a3c 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -489,8 +489,7 @@ static inline void fill_expolygon_generate_paths( extrusion_entities_append_paths( dst, std::move(polylines), - role, - flow.mm3_per_mm(), flow.width(), flow.height()); + { role, flow }); } static inline void fill_expolygons_generate_paths( @@ -648,7 +647,7 @@ static inline void tree_supports_generate_paths( ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); if (level2.size() == 1) { Polylines polylines; - extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), { ExtrusionRole::SupportMaterial, flow }, // Disable reversal of the path, always start with the anchor, always print CCW. false); expoly = level2.front(); @@ -754,7 +753,7 @@ static inline void tree_supports_generate_paths( } ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; - extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(out, std::move(polylines), { ExtrusionRole::SupportMaterial, flow }, // Disable reversal of the path, always start with the anchor, always print CCW. false); if (eec) { @@ -798,7 +797,7 @@ static inline void fill_expolygons_with_sheath_generate_paths( eec->no_sort = true; } ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; - extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); + extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), { ExtrusionRole::SupportMaterial, flow }); // Fill in the rest. fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); if (no_sort && ! eec->empty()) @@ -1110,7 +1109,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact extrusion_entities_append_paths( top_contact_layer.extrusions, std::move(loop_lines), - ExtrusionRole::SupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); + { ExtrusionRole::SupportMaterialInterface, flow }); } #ifdef SLIC3R_DEBUG @@ -1152,24 +1151,19 @@ static void modulate_extrusion_by_overlapping_layers( ExtrusionPath *extrusion_path_template = dynamic_cast(extrusions_in_out.front()); assert(extrusion_path_template != nullptr); ExtrusionRole extrusion_role = extrusion_path_template->role(); - float extrusion_width = extrusion_path_template->width; + float extrusion_width = extrusion_path_template->width(); struct ExtrusionPathFragment { - ExtrusionPathFragment() : mm3_per_mm(-1), width(-1), height(-1) {}; - ExtrusionPathFragment(double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height) {}; - + ExtrusionFlow flow; Polylines polylines; - double mm3_per_mm; - float width; - float height; }; // Split the extrusions by the overlapping layers, reduce their extrusion rate. // The last path_fragment is from this_layer. std::vector path_fragments( n_overlapping_layers + 1, - ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); + ExtrusionPathFragment{ extrusion_path_template->attributes() }); // Don't use it, it will be released. extrusion_path_template = nullptr; @@ -1246,8 +1240,8 @@ static void modulate_extrusion_by_overlapping_layers( path_fragments.back().polylines = diff_pl(path_fragments.back().polylines, polygons_trimming); // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). assert(this_layer.print_z > overlapping_layer.print_z); - frag.height = float(this_layer.print_z - overlapping_layer.print_z); - frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); + frag.flow.height = float(this_layer.print_z - overlapping_layer.print_z); + frag.flow.mm3_per_mm = Flow(frag.flow.width, frag.flow.height, -1.f).mm3_per_mm(); #ifdef SLIC3R_DEBUG svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); #endif /* SLIC3R_DEBUG */ @@ -1330,15 +1324,14 @@ static void modulate_extrusion_by_overlapping_layers( ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back(); if (path != nullptr) { // Verify whether the path is compatible with the current fragment. - assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); - if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm) { + assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height() != frag.flow.height || path->mm3_per_mm() != frag.flow.mm3_per_mm); + if (path->height() != frag.flow.height || path->mm3_per_mm() != frag.flow.mm3_per_mm) path = nullptr; - } // Merging with the previous path. This can only happen if the current layer was reduced by a base layer, which was split into a base and interface layer. } if (path == nullptr) { // Allocate a new path. - multipath.paths.push_back(ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height)); + multipath.paths.emplace_back(ExtrusionAttributes{ extrusion_role, frag.flow }); path = &multipath.paths.back(); } // The Clipper library may flip the order of the clipped polylines arbitrarily. @@ -1373,8 +1366,8 @@ static void modulate_extrusion_by_overlapping_layers( } // If there are any non-consumed fragments, add them separately. //FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected. - for (auto it_fragment = path_fragments.begin(); it_fragment != path_fragments.end(); ++ it_fragment) - extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); + for (ExtrusionPathFragment &fragment : path_fragments) + extrusion_entities_append_paths(extrusions_in_out, std::move(fragment.polylines), { extrusion_role, fragment.flow }); } // Support layer that is covered by some form of dense interface. @@ -1736,7 +1729,8 @@ void generate_support_toolpaths( // Filler and its parameters filler, float(density), // Extrusion parameters - ExtrusionRole::SupportMaterialInterface, interface_flow); + interface_as_base ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, + interface_flow); } }; const bool top_interfaces = config.support_material_interface_layers.value != 0; diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 10d29340cd..937a4bf5df 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1035,7 +1035,7 @@ namespace SupportMaterialInternal { assert(expansion_scaled >= 0.f); for (const ExtrusionPath &ep : loop.paths) if (ep.role() == ExtrusionRole::OverhangPerimeter && ! ep.polyline.empty()) { - float exp = 0.5f * (float)scale_(ep.width) + expansion_scaled; + float exp = 0.5f * (float)scale_(ep.width()) + expansion_scaled; if (ep.is_closed()) { if (ep.size() >= 3) { // This is a complete loop. diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index dbc1124ae7..b12cc8a2fa 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -105,6 +105,7 @@ static inline void validate_range(const LineInformations &lines) validate_range(l); } +/* static inline void check_self_intersections(const Polygons &polygons, const std::string_view message) { #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 @@ -112,6 +113,7 @@ static inline void check_self_intersections(const Polygons &polygons, const std: ::MessageBoxA(nullptr, (std::string("TreeSupport infill self intersections: ") + std::string(message)).c_str(), "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); #endif // TREE_SUPPORT_SHOW_ERRORS_WIN32 } +*/ static inline void check_self_intersections(const ExPolygon &expoly, const std::string_view message) { #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 0f3ab708dc..fd5bd7d5e8 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -332,10 +332,10 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; } - const float flow_width = get_flow_width(layer_region, entity->role()); - std::vector annotated_points = estimate_points_properties(entity->as_polyline().points, - prev_layer_boundary, flow_width, - params.bridge_distance); + const float flow_width = get_flow_width(layer_region, entity->role()); + std::vector annotated_points = + ExtrusionProcessor::estimate_points_properties(entity->as_polyline().points, prev_layer_boundary, + flow_width, params.bridge_distance); std::vector lines_out; lines_out.reserve(annotated_points.size()); @@ -344,8 +344,8 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit std::optional bridging_dir{}; for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - const ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + ExtrusionProcessor::ExtendedPoint &curr_point = annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; SupportPointCause potential_cause = std::abs(curr_point.curvature) > 0.1 ? SupportPointCause::FloatingBridgeAnchor : SupportPointCause::LongBridge; @@ -387,19 +387,19 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit const float flow_width = get_flow_width(layer_region, entity->role()); // Compute only unsigned distance - prev_layer_lines can contain unconnected paths, thus the sign of the distance is unreliable - std::vector annotated_points = estimate_points_properties(entity->as_polyline().points, - prev_layer_lines, flow_width, - params.bridge_distance); + std::vector annotated_points = + ExtrusionProcessor::estimate_points_properties(entity->as_polyline().points, prev_layer_lines, + flow_width, params.bridge_distance); std::vector lines_out; lines_out.reserve(annotated_points.size()); float bridged_distance = annotated_points.front().position != annotated_points.back().position ? (params.bridge_distance + 1.0f) : 0.0f; for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - const ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - float line_len = (prev_point.position - curr_point.position).norm(); - ExtrusionLine line_out{prev_point.position.cast(), curr_point.position.cast(), line_len, entity}; + ExtrusionProcessor::ExtendedPoint &curr_point = annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + float line_len = (prev_point.position - curr_point.position).norm(); + ExtrusionLine line_out{prev_point.position.cast(), curr_point.position.cast(), line_len, entity}; Vec2f middle = 0.5 * (line_out.a + line_out.b); auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra(middle); @@ -1112,12 +1112,13 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, Polygon pol(pl.points); pol.make_counter_clockwise(); - auto annotated_points = estimate_points_properties(pol.points, prev_layer_lines, flow_width); + auto annotated_points = ExtrusionProcessor::estimate_points_properties(pol.points, prev_layer_lines, + flow_width); for (size_t i = 0; i < annotated_points.size(); ++i) { - const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - const ExtendedPoint &b = annotated_points[i]; - ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + const ExtrusionProcessor::ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; Vec2f middle = 0.5 * (line_out.a + line_out.b); @@ -1187,11 +1188,13 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) Points extrusion_pts; extrusion->collect_points(extrusion_pts); float flow_width = get_flow_width(layer_region, extrusion->role()); - auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, - params.bridge_distance); + auto annotated_points = ExtrusionProcessor::estimate_points_properties(extrusion_pts, + prev_layer_lines, + flow_width, + params.bridge_distance); for (size_t i = 0; i < annotated_points.size(); ++i) { - const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - const ExtendedPoint &b = annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &b = annotated_points[i]; ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; @@ -1201,7 +1204,8 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) prev_layer_lines.get_line(bottom_line_idx); // correctify the distance sign using slice polygons - float sign = (prev_layer_boundary.distance_from_lines(middle.cast()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; + float sign = (prev_layer_boundary.distance_from_lines(middle.cast()) + 0.5f * flow_width) < 0.0f ? -1.0f : + 1.0f; line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height, flow_width, bottom_line.curled_up_height, params); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 7be1d0028e..6c295279ad 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,4 +58,7 @@ // Enable OpenGL debug messages using debug context #define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE) +// Enable imgui dialog which allows to set the parameters used to export binarized gcode +#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW 0 + #endif // _prusaslicer_technologies_h_ diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 02fdfd31f2..6a1c412147 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -1943,7 +1943,7 @@ std::vector slice_mesh_ex( &expolygons); #if 0 -//#ifndef _NDEBUG +//#ifndef NDEBUG // Test whether the expolygons in a single layer overlap. for (size_t i = 0; i < expolygons.size(); ++ i) for (size_t j = i + 1; j < expolygons.size(); ++ j) { @@ -1952,7 +1952,7 @@ std::vector slice_mesh_ex( } #endif #if 0 -//#ifndef _NDEBUG +//#ifndef NDEBUG for (const ExPolygon &ex : expolygons) { assert(! has_duplicate_points(ex.contour)); for (const Polygon &hole : ex.holes) @@ -1960,7 +1960,7 @@ std::vector slice_mesh_ex( assert(! has_duplicate_points(ex)); } assert(!has_duplicate_points(expolygons)); -#endif // _NDEBUG +#endif // NDEBUG //FIXME simplify if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) keep_largest_contour_only(expolygons); @@ -1972,7 +1972,7 @@ std::vector slice_mesh_ex( expolygons = std::move(simplified); } #if 0 -//#ifndef _NDEBUG +//#ifndef NDEBUG for (const ExPolygon &ex : expolygons) { assert(! has_duplicate_points(ex.contour)); for (const Polygon &hole : ex.holes) @@ -1980,7 +1980,7 @@ std::vector slice_mesh_ex( assert(! has_duplicate_points(ex)); } assert(! has_duplicate_points(expolygons)); -#endif // _NDEBUG +#endif // NDEBUG } }); // BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end"; diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index aa3bb4ce61..e02126d9ba 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -58,6 +58,11 @@ const std::string& sys_shapes_dir(); // Return a full path to the custom shapes gallery directory. std::string custom_shapes_dir(); +// Set a path with shapes gallery files. +void set_custom_gcodes_dir(const std::string &path); +// Return a full path to the system shapes gallery directory. +const std::string& custom_gcodes_dir(); + // Set a path with preset files. void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index b93d2d36a0..09fa98ca1f 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -199,6 +199,18 @@ const std::string& sys_shapes_dir() return g_sys_shapes_dir; } +static std::string g_custom_gcodes_dir; + +void set_custom_gcodes_dir(const std::string &dir) +{ + g_custom_gcodes_dir = dir; +} + +const std::string& custom_gcodes_dir() +{ + return g_custom_gcodes_dir; +} + // Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one. Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr; @@ -786,8 +798,9 @@ bool is_idx_file(const boost::filesystem::directory_entry &dir_entry) bool is_gcode_file(const std::string &path) { - return boost::iends_with(path, ".gcode") || boost::iends_with(path, ".gco") || - boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc"); + return boost::iends_with(path, ".gcode") || boost::iends_with(path, ".gco") || + boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc") || + boost::iends_with(path, ".bgcode") || boost::iends_with(path, ".bgc"); } bool is_img_file(const std::string &path) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 76397bbdbf..274f3ccfec 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -120,6 +120,8 @@ set(SLIC3R_GUI_SOURCES GUI/PresetComboBoxes.cpp GUI/BitmapComboBox.hpp GUI/BitmapComboBox.cpp + GUI/EditGCodeDialog.hpp + GUI/EditGCodeDialog.cpp GUI/SavePresetDialog.hpp GUI/SavePresetDialog.cpp GUI/PhysicalPrinterDialog.hpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 87275b0b58..d321b62f4a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1453,8 +1453,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, flo polyline.remove_duplicate_points(); polyline.translate(copy); const Lines lines = polyline.lines(); - std::vector widths(lines.size(), extrusion_path.width); - std::vector heights(lines.size(), extrusion_path.height); + std::vector widths(lines.size(), extrusion_path.width()); + std::vector heights(lines.size(), extrusion_path.height()); thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); } @@ -1470,8 +1470,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, flo polyline.translate(copy); const Lines lines_this = polyline.lines(); append(lines, lines_this); - widths.insert(widths.end(), lines_this.size(), extrusion_path.width); - heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width()); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height()); } thick_lines_to_verts(lines, widths, heights, true, print_z, geometry); } @@ -1488,8 +1488,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_mult polyline.translate(copy); const Lines lines_this = polyline.lines(); append(lines, lines_this); - widths.insert(widths.end(), lines_this.size(), extrusion_path.width); - heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width()); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height()); } thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index e6abeda550..cd5248d1a1 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -345,6 +345,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("min_feature_size", have_arachne); toggle_field("min_bead_width", have_arachne); toggle_field("thin_walls", !have_arachne); + + bool has_arc_fitting = config->opt_enum("arc_fitting") != ArcFittingType::Disabled; + toggle_field("arc_fitting_tolerance", has_arc_fitting); } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 4b967d2238..741ab7be00 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -786,6 +786,16 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin reload_presets(); set_compatible_printers_html_window(std::vector(), false); } + +void PageMaterials::check_and_update_presets(bool force_reload_presets /*= false*/) +{ + if (presets_loaded) + return; + wizard_p()->update_materials(materials->technology); + if (force_reload_presets) + reload_presets(); +} + void PageMaterials::on_paint() { } @@ -1299,10 +1309,7 @@ void PageMaterials::clear() void PageMaterials::on_activate() { - if (! presets_loaded) { - wizard_p()->update_materials(materials->technology); - reload_presets(); - } + check_and_update_presets(true); first_paint = true; } @@ -2369,7 +2376,7 @@ void ConfigWizard::priv::load_pages() return a.first < b.first; }); - for (const std::pair v : sorted_vendors) { + for (const std::pair &v : sorted_vendors) { const auto& pages = pages_3rdparty.find(v.second); if (pages == pages_3rdparty.end()) continue; // Should not happen @@ -2507,7 +2514,7 @@ void ConfigWizard::priv::load_vendors() } } appconfig_new.set_section(section_name, section_new); - }; + } } void ConfigWizard::priv::add_page(ConfigWizardPage *page) @@ -2589,78 +2596,37 @@ void ConfigWizard::priv::set_run_reason(RunReason run_reason) void ConfigWizard::priv::update_materials(Technology technology) { + auto add_material = [](Materials& materials, PresetAliases& aliases, const Preset& preset, const Preset* printer = nullptr) { + if (!materials.containts(&preset)) { + materials.push(&preset); + if (!preset.alias.empty()) + aliases[preset.alias].emplace(&preset); + } + if (printer) { + materials.add_printer(printer); + materials.compatibility_counter[preset.alias]++; + } + }; + if ((any_fff_selected || custom_printer_in_bundle || custom_printer_selected) && (technology & T_FFF)) { filaments.clear(); aliases_fff.clear(); - // Iterate filaments in all bundles - for (const auto &pair : bundles) { - for (const auto &filament : pair.second.preset_bundle->filaments) { - // Check if filament is already added - if (filaments.containts(&filament)) - continue; + + for (const auto &[name, bundle] : bundles) { + for (const auto &filament : bundle.preset_bundle->filaments) { // Iterate printers in all bundles - for (const auto &printer : pair.second.preset_bundle->printers) { + for (const auto &printer : bundle.preset_bundle->printers) { if (!printer.is_visible || printer.printer_technology() != ptFFF) continue; // Filter out inapplicable printers - if (is_compatible_with_printer(PresetWithVendorProfile(filament, filament.vendor), PresetWithVendorProfile(printer, printer.vendor))) { - if (!filaments.containts(&filament)) { - filaments.push(&filament); - if (!filament.alias.empty()) - aliases_fff[filament.alias].insert(filament.name); - } - filaments.add_printer(&printer); - } + if (is_compatible_with_printer(PresetWithVendorProfile(filament, filament.vendor), PresetWithVendorProfile(printer, printer.vendor))) + add_material(filaments, aliases_fff, filament, &printer); } // template filament bundle has no printers - filament would be never added - if(pair.second.vendor_profile&& pair.second.vendor_profile->templates_profile && pair.second.preset_bundle->printers.begin() == pair.second.preset_bundle->printers.end()) - { - if (!filaments.containts(&filament)) { - filaments.push(&filament); - if (!filament.alias.empty()) - aliases_fff[filament.alias].insert(filament.name); - } - } + if(bundle.vendor_profile && bundle.vendor_profile->templates_profile && bundle.preset_bundle->printers.begin() == bundle.preset_bundle->printers.end()) + add_material(filaments, aliases_fff, filament); } } - // count compatible printers - for (const auto& preset : filaments.presets) { - // skip template filaments - if (preset->vendor && preset->vendor->templates_profile) - continue; - - const auto filter = [preset](const std::pair element) { - return preset->alias == element.first; - }; - if (std::find_if(filaments.compatibility_counter.begin(), filaments.compatibility_counter.end(), filter) != filaments.compatibility_counter.end()) { - continue; - } - // find all aliases (except templates) - std::vector idx_with_same_alias; - for (size_t i = 0; i < filaments.presets.size(); ++i) { - if (preset->alias == filaments.presets[i]->alias && ((filaments.presets[i]->vendor && !filaments.presets[i]->vendor->templates_profile) || !filaments.presets[i]->vendor)) - idx_with_same_alias.push_back(i); - } - // check compatibility with each printer - size_t counter = 0; - for (const auto& printer : filaments.printers) { - if (!(*printer).is_visible || (*printer).printer_technology() != ptFFF) - continue; - bool compatible = false; - // Test other materials with same alias - for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) { - const Preset& prst = *(filaments.presets[idx_with_same_alias[i]]); - const Preset& prntr = *printer; - if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) { - compatible = true; - break; - } - } - if (compatible) - counter++; - } - filaments.compatibility_counter.emplace_back(preset->alias, counter); - } } if (any_sla_selected && (technology & T_SLA)) { @@ -2668,62 +2634,20 @@ void ConfigWizard::priv::update_materials(Technology technology) aliases_sla.clear(); // Iterate SLA materials in all bundles - for (const auto &pair : bundles) { - for (const auto &material : pair.second.preset_bundle->sla_materials) { - // Check if material is already added - if (sla_materials.containts(&material)) - continue; + for (const auto& [name, bundle] : bundles) { + for (const auto &material : bundle.preset_bundle->sla_materials) { // Iterate printers in all bundles // For now, we only allow the profiles to be compatible with another profiles inside the same bundle. - for (const auto& printer : pair.second.preset_bundle->printers) { + for (const auto& printer : bundle.preset_bundle->printers) { if(!printer.is_visible || printer.printer_technology() != ptSLA) continue; // Filter out inapplicable printers - if (is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr))) { + if (is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr))) // Check if material is already added - if(!sla_materials.containts(&material)) { - sla_materials.push(&material); - if (!material.alias.empty()) - aliases_sla[material.alias].insert(material.name); - } - sla_materials.add_printer(&printer); - } + add_material(sla_materials, aliases_sla, material, &printer); } } } - // count compatible printers - for (const auto& preset : sla_materials.presets) { - - const auto filter = [preset](const std::pair element) { - return preset->alias == element.first; - }; - if (std::find_if(sla_materials.compatibility_counter.begin(), sla_materials.compatibility_counter.end(), filter) != sla_materials.compatibility_counter.end()) { - continue; - } - std::vector idx_with_same_alias; - for (size_t i = 0; i < sla_materials.presets.size(); ++i) { - if(preset->alias == sla_materials.presets[i]->alias) - idx_with_same_alias.push_back(i); - } - size_t counter = 0; - for (const auto& printer : sla_materials.printers) { - if (!(*printer).is_visible || (*printer).printer_technology() != ptSLA) - continue; - bool compatible = false; - // Test otrher materials with same alias - for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) { - const Preset& prst = *(sla_materials.presets[idx_with_same_alias[i]]); - const Preset& prntr = *printer; - if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) { - compatible = true; - break; - } - } - if (compatible) - counter++; - } - sla_materials.compatibility_counter.emplace_back(preset->alias, counter); - } } } @@ -2857,21 +2781,15 @@ bool ConfigWizard::priv::on_bnt_finish() index->go_to(page_downloader); return false; } - /* When Filaments or Sla Materials pages are activated, - * materials for this pages are automaticaly updated and presets are reloaded. - * - * But, if _Finish_ button was clicked without activation of those pages - * (for example, just some printers were added/deleted), - * than last changes wouldn't be updated for filaments/materials. - * SO, do that before close of Wizard - */ - update_materials(T_ANY); - if (any_fff_selected) - page_filaments->reload_presets(); - if (any_sla_selected) - page_sla_materials->reload_presets(); - // theres no need to check that filament is selected if we have only custom printer + /* If some printers were added/deleted, but related MaterialPage wasn't activated, + * than last changes wouldn't be updated for filaments/materials. + * SO, do that before check_and_install_missing_materials() + */ + page_filaments->check_and_update_presets(); + page_sla_materials->check_and_update_presets(); + + // there's no need to check that filament is selected if we have only custom printer if (custom_printer_selected && !any_fff_selected && !any_sla_selected) return true; // check, that there is selected at least one filament/material return check_and_install_missing_materials(T_ANY); @@ -3357,8 +3275,8 @@ void ConfigWizard::priv::update_presets_in_config(const std::string& section, co // add or delete presets had a same alias auto it = aliases.find(alias_key); if (it != aliases.end()) - for (const std::string& name : it->second) - update(section, name); + for (const Preset* preset : it->second) + update(section, preset->name); } bool ConfigWizard::priv::check_fff_selected() diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index ea4701cd67..ce43fd5e71 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -296,6 +296,7 @@ struct PageMaterials: ConfigWizardPage PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name); + void check_and_update_presets(bool force_reload_presets = false); void reload_presets(); void update_lists(int sel_type, int sel_vendor, int last_selected_printer = -1); void on_material_highlighted(int sel_material); @@ -321,8 +322,8 @@ struct Materials Technology technology; // use vector for the presets to purpose of save of presets sorting in the bundle std::vector presets; - // String is alias of material, size_t number of compatible counters - std::vector> compatibility_counter; + // String is alias of material, size_t number of compatible printers counters + std::map compatibility_counter; std::set types; std::set printers; @@ -579,7 +580,7 @@ wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); // ConfigWizard private data -typedef std::map> PresetAliases; +typedef std::map> PresetAliases; struct ConfigWizard::priv { @@ -592,14 +593,14 @@ struct ConfigWizard::priv // PrinterPickers state. Materials filaments; // Holds available filament presets and their types & vendors Materials sla_materials; // Ditto for SLA materials - PresetAliases aliases_fff; // Map of aliase to preset names - PresetAliases aliases_sla; // Map of aliase to preset names + PresetAliases aliases_fff; // Map of alias to material presets + PresetAliases aliases_sla; // Map of alias to material presets std::unique_ptr custom_config; // Backing for custom printer definition bool any_fff_selected; // Used to decide whether to display Filaments page bool any_sla_selected; // Used to decide whether to display SLA Materials page bool custom_printer_selected { false }; // New custom printer is requested bool custom_printer_in_bundle { false }; // Older custom printer already exists when wizard starts - // Set to true if there are none FFF printers on the main FFF page. If true, only SLA printers are shown (not even custum printers) + // Set to true if there are none FFF printers on the main FFF page. If true, only SLA printers are shown (not even custom printers) bool only_sla_mode { false }; bool template_profile_selected { false }; // This bool has one purpose - to tell that template profile should be installed if its not (because it cannot be added to appconfig) diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp new file mode 100644 index 0000000000..26ad5e827f --- /dev/null +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -0,0 +1,666 @@ +#include "EditGCodeDialog.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Tab.hpp" +#include "wxExtensions.hpp" +#include "BitmapCache.hpp" +#include "ExtraRenderers.hpp" +#include "MsgDialog.hpp" +#include "Plater.hpp" + +#include "libslic3r/PlaceholderParser.hpp" +#include "libslic3r/Preset.hpp" +#include "libslic3r/Print.hpp" + +namespace Slic3r { +namespace GUI { + +//------------------------------------------ +// EditGCodeDialog +//------------------------------------------ + +EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const std::string& value) : + DPIDialog(parent, wxID_ANY, format_wxstr(_L("Edit Custom G-code (%1%)"), key), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + SetFont(wxGetApp().normal_font()); + wxGetApp().UpdateDarkUI(this); + + int border = 10; + int em = em_unit(); + + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Built-in placeholders (Double click item to add to G-code)") + ":"); + + auto* grid_sizer = new wxFlexGridSizer(1, 3, 5, 15); + grid_sizer->SetFlexibleDirection(wxBOTH); + + m_params_list = new ParamsViewCtrl(this, wxSize(em * 30, em * 70)); + m_params_list->SetFont(wxGetApp().code_font()); + wxGetApp().UpdateDarkUI(m_params_list); + + m_add_btn = new ScalableButton(this, wxID_ANY, "add_copies"); + m_add_btn->SetToolTip(_L("Add selected placeholder to G-code")); + + m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 75, em * 70), wxTE_MULTILINE +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + ); + m_gcode_editor->SetFont(wxGetApp().code_font()); + wxGetApp().UpdateDarkUI(m_gcode_editor); + + grid_sizer->Add(m_params_list, 1, wxEXPAND); + grid_sizer->Add(m_add_btn, 0, wxALIGN_CENTER_VERTICAL); + grid_sizer->Add(m_gcode_editor, 2, wxEXPAND); + + grid_sizer->AddGrowableRow(0, 1); + grid_sizer->AddGrowableCol(0, 1); + grid_sizer->AddGrowableCol(2, 1); + + m_param_label = new wxStaticText(this, wxID_ANY, _L("Select placeholder")); + m_param_label->SetFont(wxGetApp().bold_font()); + + m_param_description = new wxStaticText(this, wxID_ANY, wxEmptyString); + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxGetApp().UpdateDarkUI(this->FindWindowById(wxID_OK, this)); + wxGetApp().UpdateDarkUI(this->FindWindowById(wxID_CANCEL, this)); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(grid_sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_param_label , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_param_description , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(btns , 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); + + this->Fit(); + this->Layout(); + + this->CenterOnScreen(); + + init_params_list(key); + bind_list_and_button(); +} + +std::string EditGCodeDialog::get_edited_gcode() const +{ + return into_u8(m_gcode_editor->GetValue()); +} + +static ParamType get_type(const std::string& opt_key, const ConfigOptionDef& opt_def) +{ + return opt_def.is_scalar() ? ParamType::Scalar : ParamType::Vector; +} + +void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) +{ + const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders(); + const auto& specific_params = custom_gcode_placeholders.count(custom_gcode_name) > 0 ? + custom_gcode_placeholders.at(custom_gcode_name) : t_config_option_keys({}); + + // Add slicing states placeholders + + wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing State"), "re_slice"); + if (!cgp_ro_slicing_states_config_def.empty()) { + wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed"); + for (const auto& [opt_key, def]: cgp_ro_slicing_states_config_def.options) + m_params_list->AppendParam(read_only, get_type(opt_key, def), opt_key); + } + + if (!cgp_rw_slicing_states_config_def.empty()) { + wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read Write"), "lock_open"); + for (const auto& [opt_key, def] : cgp_rw_slicing_states_config_def.options) + m_params_list->AppendParam(read_write, get_type(opt_key, def), opt_key); + } + + // add other universal params, which are related to slicing state + + if (!cgp_other_slicing_states_config_def.empty()) { + slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); + for (const auto& [opt_key, def] : cgp_other_slicing_states_config_def.options) + m_params_list->AppendParam(slicing_state, get_type(opt_key, def), opt_key); + } + + // Add universal placeholders + + { + // Add print statistics subgroup + + if (!cgp_print_statistics_config_def.empty()) { + wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print Statistics"), "info"); + for (const auto& [opt_key, def] : cgp_print_statistics_config_def.options) + m_params_list->AppendParam(statistics, get_type(opt_key, def), opt_key); + } + + // Add objects info subgroup + + if (!cgp_objects_info_config_def.empty()) { + wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects Info"), "advanced_plus"); + for (const auto& [opt_key, def] : cgp_objects_info_config_def.options) + m_params_list->AppendParam(objects_info, get_type(opt_key, def), opt_key); + } + + // Add dimensions subgroup + + if (!cgp_dimensions_config_def.empty()) { + wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Dimensions"), "measure"); + for (const auto& [opt_key, def] : cgp_dimensions_config_def.options) + m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key); + } + + // Add timestamp subgroup + + if (!cgp_timestamps_config_def.empty()) { + wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Timestamps"), "time"); + for (const auto& [opt_key, def] : cgp_timestamps_config_def.options) + m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key); + } + } + + // Add specific placeholders + + if (!specific_params.empty()) { + wxDataViewItem group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "add_gcode"); + for (const auto& opt_key : specific_params) + if (custom_gcode_specific_config_def.has(opt_key)) { + auto def = custom_gcode_specific_config_def.get(opt_key); + m_params_list->AppendParam(group, get_type(opt_key, *def), opt_key); + } + m_params_list->Expand(group); + } + + // Add placeholders from presets + + wxDataViewItem presets = add_presets_placeholders(); + // add other params which are related to presets + if (!cgp_other_presets_config_def.empty()) + for (const auto& [opt_key, def] : cgp_other_presets_config_def.options) + m_params_list->AppendParam(presets, get_type(opt_key, def), opt_key); +} + +wxDataViewItem EditGCodeDialog::add_presets_placeholders() +{ + auto get_set_from_vec = [](const std::vector&vec) { + return std::set(vec.begin(), vec.end()); + }; + + const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF; + const std::set print_options = get_set_from_vec(is_fff ? Preset::print_options() : Preset::sla_print_options()); + const std::set material_options = get_set_from_vec(is_fff ? Preset::filament_options() : Preset::sla_material_options()); + const std::set printer_options = get_set_from_vec(is_fff ? Preset::printer_options() : Preset::sla_printer_options()); + + const auto&full_config = wxGetApp().preset_bundle->full_config(); + + wxDataViewItem group = m_params_list->AppendGroup(_L("Presets"), "cog"); + + wxDataViewItem print = m_params_list->AppendSubGroup(group, _L("Print settings"), "cog"); + for (const auto&opt : print_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(print, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + wxDataViewItem material = m_params_list->AppendSubGroup(group, _(is_fff ? L("Filament settings") : L("SLA Materials settings")), is_fff ? "spool" : "resin"); + for (const auto&opt : material_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(material, optptr->is_scalar() ? ParamType::Scalar : ParamType::FilamentVector, opt); + + wxDataViewItem printer = m_params_list->AppendSubGroup(group, _L("Printer settings"), is_fff ? "printer" : "sla_printer"); + for (const auto&opt : printer_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(printer, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + return group; +} + +void EditGCodeDialog::add_selected_value_to_gcode() +{ + const wxString val = m_params_list->GetSelectedValue(); + if (!val.IsEmpty()) + m_gcode_editor->WriteText(val + "\n"); +} + +void EditGCodeDialog::bind_list_and_button() +{ + m_params_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& evt) { + wxString label; + wxString description; + + const std::string opt_key = m_params_list->GetSelectedParamKey(); + if (!opt_key.empty()) { + const ConfigOptionDef* def { nullptr }; + + const auto& full_config = wxGetApp().preset_bundle->full_config(); + if (const ConfigDef* config_def = full_config.def(); config_def && config_def->has(opt_key)) { + def = config_def->get(opt_key); + } + else { + for (const ConfigDef* config: std::initializer_list { + &custom_gcode_specific_config_def, + &cgp_ro_slicing_states_config_def, + &cgp_rw_slicing_states_config_def, + &cgp_other_slicing_states_config_def, + &cgp_print_statistics_config_def, + &cgp_objects_info_config_def, + &cgp_dimensions_config_def, + &cgp_timestamps_config_def, + &cgp_other_presets_config_def + }) { + if (config->has(opt_key)) { + def = config->get(opt_key); + break; + } + } + } + + if (def) { + const ConfigOptionType scalar_type = def->is_scalar() ? def->type : static_cast(def->type - coVectorType); + wxString type_str = scalar_type == coNone ? "none" : + scalar_type == coFloat ? "float" : + scalar_type == coInt ? "integer" : + scalar_type == coString ? "string" : + scalar_type == coPercent ? "percent" : + scalar_type == coFloatOrPercent ? "float or percent" : + scalar_type == coPoint ? "point" : + scalar_type == coBool ? "bool" : + scalar_type == coEnum ? "enum" : "undef"; + if (!def->is_scalar()) + type_str += "[]"; + + label = (!def || (def->full_label.empty() && def->label.empty()) ) ? format_wxstr("%1%\n(%2%)", opt_key, type_str) : + (!def->full_label.empty() && !def->label.empty() ) ? + format_wxstr("%1% > %2%\n(%3%)", _(def->full_label), _(def->label), type_str) : + format_wxstr("%1%\n(%2%)", def->label.empty() ? _(def->full_label) : _(def->label), type_str); + + if (def) + description = get_wraped_wxString(_(def->tooltip), 120); + } + else + label = "Undef optptr"; + } + + m_param_label->SetLabel(label); + m_param_description->SetLabel(description); + + Layout(); + }); + + m_params_list->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& ) { + add_selected_value_to_gcode(); + }); + + m_add_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + add_selected_value_to_gcode(); + }); +} + +void EditGCodeDialog::on_dpi_changed(const wxRect&suggested_rect) +{ + const int& em = em_unit(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + const wxSize& size = wxSize(45 * em, 35 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void EditGCodeDialog::on_sys_color_changed() +{ + m_add_btn->sys_color_changed(); +} + + + +const std::map ParamsInfo { +// Type BitmapName + { ParamType::Scalar, "scalar_param" }, + { ParamType::Vector, "vector_param" }, + { ParamType::FilamentVector,"vector_filament_param" }, +}; + +static void make_bold(wxString& str) +{ +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) + str = format_wxstr("%1%", str); +#endif +} + +// ---------------------------------------------------------------------------- +// ParamsModelNode: a node inside ParamsModel +// ---------------------------------------------------------------------------- + +ParamsNode::ParamsNode(const wxString& group_name, const std::string& icon_name) +: icon_name(icon_name) +, text(group_name) +{ + make_bold(text); +} + +ParamsNode::ParamsNode( ParamsNode * parent, + const wxString& sub_group_name, + const std::string& icon_name) + : m_parent(parent) + , icon_name(icon_name) + , text(sub_group_name) +{ + make_bold(text); +} + +ParamsNode::ParamsNode( ParamsNode* parent, + ParamType param_type, + const std::string& param_key) + : m_parent(parent) + , m_param_type(param_type) + , m_container(false) + , param_key(param_key) +{ + text = from_u8(param_key); + if (param_type == ParamType::Vector) + text += "[]"; + else if (param_type == ParamType::FilamentVector) + text += "[current_extruder]"; + + icon_name = ParamsInfo.at(param_type); +} + + +// ---------------------------------------------------------------------------- +// ParamsModel +// ---------------------------------------------------------------------------- + +ParamsModel::ParamsModel() +{ +} + +wxDataViewItem ParamsModel::AppendGroup(const wxString& group_name, + const std::string& icon_name) +{ + m_group_nodes.emplace_back(std::make_unique(group_name, icon_name)); + + wxDataViewItem parent(nullptr); + wxDataViewItem child((void*)m_group_nodes.back().get()); + + ItemAdded(parent, child); + m_ctrl->Expand(parent); + return child; +} + +wxDataViewItem ParamsModel::AppendSubGroup(wxDataViewItem parent, + const wxString& sub_group_name, + const std::string& icon_name) +{ + ParamsNode* parent_node = static_cast(parent.GetID()); + if (!parent_node) + return wxDataViewItem(0); + + parent_node->Append(std::make_unique(parent_node, sub_group_name, icon_name)); + const wxDataViewItem sub_group_item((void*)parent_node->GetChildren().back().get()); + + ItemAdded(parent, sub_group_item); + return sub_group_item; +} + +wxDataViewItem ParamsModel::AppendParam(wxDataViewItem parent, + ParamType param_type, + const std::string& param_key) +{ + ParamsNode* parent_node = static_cast(parent.GetID()); + if (!parent_node) + return wxDataViewItem(0); + + parent_node->Append(std::make_unique(parent_node, param_type, param_key)); + + const wxDataViewItem child_item((void*)parent_node->GetChildren().back().get()); + + ItemAdded(parent, child_item); + return child_item; +} + +wxString ParamsModel::GetParamName(wxDataViewItem item) +{ + if (item.IsOk()) { + ParamsNode* node = static_cast(item.GetID()); + if (node->IsParamNode()) + return node->text; + } + return wxEmptyString; +} + +std::string ParamsModel::GetParamKey(wxDataViewItem item) +{ + if (item.IsOk()) { + ParamsNode* node = static_cast(item.GetID()); + return node->param_key; + } + return std::string(); +} + +wxDataViewItem ParamsModel::Delete(const wxDataViewItem& item) +{ + auto ret_item = wxDataViewItem(nullptr); + ParamsNode* node = static_cast(item.GetID()); + if (!node) // happens if item.IsOk()==false + return ret_item; + + // first remove the node from the parent's array of children; + // NOTE: m_group_nodes is only a vector of _pointers_ + // thus removing the node from it doesn't result in freeing it + ParamsNodePtrArray& children = node->GetChildren(); + // Delete all children + while (!children.empty()) + Delete(wxDataViewItem(children.back().get())); + + auto node_parent = node->GetParent(); + + ParamsNodePtrArray& parents_children = node_parent ? node_parent->GetChildren() : m_group_nodes; + auto it = find_if(parents_children.begin(), parents_children.end(), + [node](std::unique_ptr& child) { return child.get() == node; }); + assert(it != parents_children.end()); + it = parents_children.erase(it); + + if (it != parents_children.end()) + ret_item = wxDataViewItem(it->get()); + + wxDataViewItem parent(node_parent); + // set m_container to FALSE if parent has no child + if (node_parent) { +#ifndef __WXGTK__ + if (node_parent->GetChildren().empty()) + node_parent->SetContainer(false); +#endif //__WXGTK__ + ret_item = parent; + } + + // notify control + ItemDeleted(parent, item); + return ret_item; +} + +void ParamsModel::Clear() +{ + while (!m_group_nodes.empty()) + Delete(wxDataViewItem(m_group_nodes.back().get())); + +} + +void ParamsModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const +{ + assert(item.IsOk()); + + ParamsNode* node = static_cast(item.GetID()); + if (col == (unsigned int)0) +#ifdef __linux__ + variant << wxDataViewIconText(node->text, get_bmp_bundle(node->icon_name)->GetIconFor(m_ctrl->GetParent())); +#else + variant << DataViewBitmapText(node->text, get_bmp_bundle(node->icon_name)->GetBitmapFor(m_ctrl->GetParent())); +#endif //__linux__ + else + wxLogError("DiffModel::GetValue: wrong column %d", col); +} + +bool ParamsModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) +{ + assert(item.IsOk()); + + ParamsNode* node = static_cast(item.GetID()); + if (col == (unsigned int)0) { +#ifdef __linux__ + wxDataViewIconText data; + data << variant; + node->icon = data.GetIcon(); +#else + DataViewBitmapText data; + data << variant; + node->icon = data.GetBitmap(); +#endif + node->text = data.GetText(); + return true; + } + + wxLogError("DiffModel::SetValue: wrong column"); + return false; +} + +wxDataViewItem ParamsModel::GetParent(const wxDataViewItem&item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(nullptr); + + ParamsNode* node = static_cast(item.GetID()); + + if (node->IsGroupNode()) + return wxDataViewItem(nullptr); + + return wxDataViewItem((void*)node->GetParent()); +} + +bool ParamsModel::IsContainer(const wxDataViewItem& item) const +{ + // the invisble root node can have children + if (!item.IsOk()) + return true; + + ParamsNode* node = static_cast(item.GetID()); + return node->IsContainer(); +} + +unsigned int ParamsModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const +{ + ParamsNode* parent_node = (ParamsNode*)parent.GetID(); + + if (parent_node == nullptr) { + for (const auto& group : m_group_nodes) + array.Add(wxDataViewItem((void*)group.get())); + } + else { + const ParamsNodePtrArray& children = parent_node->GetChildren(); + for (const std::unique_ptr& child : children) + array.Add(wxDataViewItem((void*)child.get())); + } + + return array.Count(); +} + + +// ---------------------------------------------------------------------------- +// ParamsViewCtrl +// ---------------------------------------------------------------------------- + +ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSize size) + : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_SINGLE | wxDV_NO_HEADER// | wxDV_ROW_LINES +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + ), + m_em_unit(em_unit(parent)) +{ + wxGetApp().UpdateDVCDarkUI(this); + + model = new ParamsModel(); + this->AssociateModel(model); + model->SetAssociatedControl(this); + +#ifdef __linux__ + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); +#ifdef SUPPORTS_MARKUP + rd->EnableMarkup(true); +#endif + wxDataViewColumn* column = new wxDataViewColumn("", rd, 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); +#else + wxDataViewColumn* column = new wxDataViewColumn("", new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); +#endif //__linux__ + this->AppendColumn(column); + this->SetExpanderColumn(column); +} + +wxDataViewItem ParamsViewCtrl::AppendGroup(const wxString& group_name, const std::string& icon_name) +{ + return model->AppendGroup(group_name, icon_name); +} + +wxDataViewItem ParamsViewCtrl::AppendSubGroup( wxDataViewItem parent, + const wxString& sub_group_name, + const std::string& icon_name) +{ + return model->AppendSubGroup(parent, sub_group_name, icon_name); +} + +wxDataViewItem ParamsViewCtrl::AppendParam( wxDataViewItem parent, + ParamType param_type, + const std::string& param_key) +{ + return model->AppendParam(parent, param_type, param_key); +} + +wxString ParamsViewCtrl::GetValue(wxDataViewItem item) +{ + return model->GetParamName(item); +} + +wxString ParamsViewCtrl::GetSelectedValue() +{ + return model->GetParamName(this->GetSelection()); +} + +std::string ParamsViewCtrl::GetSelectedParamKey() +{ + return model->GetParamKey(this->GetSelection()); +} + +void ParamsViewCtrl::CheckAndDeleteIfEmpty(wxDataViewItem item) +{ + wxDataViewItemArray children; + model->GetChildren(item, children); + if (children.IsEmpty()) + model->Delete(item); +} + +void ParamsViewCtrl::Clear() +{ + model->Clear(); +} + +void ParamsViewCtrl::Rescale(int em/* = 0*/) +{ +// model->Rescale(); + Refresh(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp new file mode 100644 index 0000000000..b54b9cc045 --- /dev/null +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -0,0 +1,232 @@ +#ifndef slic3r_EditGCodeDialog_hpp_ +#define slic3r_EditGCodeDialog_hpp_ + +#include + +#include + +#include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "libslic3r/Preset.hpp" +#include "libslic3r/PrintConfig.hpp" + +class wxListBox; +class wxTextCtrl; +class ScalableButton; +class wxStaticText; + +namespace Slic3r { + +namespace GUI { + +class ParamsViewCtrl; + +//------------------------------------------ +// EditGCodeDialog +//------------------------------------------ + +class EditGCodeDialog : public DPIDialog +{ + ParamsViewCtrl* m_params_list {nullptr}; + ScalableButton* m_add_btn {nullptr}; + wxTextCtrl* m_gcode_editor {nullptr}; + wxStaticText* m_param_label {nullptr}; + wxStaticText* m_param_description {nullptr}; + + ReadOnlySlicingStatesConfigDef cgp_ro_slicing_states_config_def; + ReadWriteSlicingStatesConfigDef cgp_rw_slicing_states_config_def; + OtherSlicingStatesConfigDef cgp_other_slicing_states_config_def; + PrintStatisticsConfigDef cgp_print_statistics_config_def; + ObjectsInfoConfigDef cgp_objects_info_config_def; + DimensionsConfigDef cgp_dimensions_config_def; + TimestampsConfigDef cgp_timestamps_config_def; + OtherPresetsConfigDef cgp_other_presets_config_def; + +public: + EditGCodeDialog(wxWindow*parent, const std::string&key, const std::string&value); + ~EditGCodeDialog() {} + + std::string get_edited_gcode() const; + + void init_params_list(const std::string& custom_gcode_name); + wxDataViewItem add_presets_placeholders(); + + void add_selected_value_to_gcode(); + void bind_list_and_button(); + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override; +}; + + + + +// ---------------------------------------------------------------------------- +// ParamsModelNode: a node inside ParamsModel +// ---------------------------------------------------------------------------- + +class ParamsNode; +using ParamsNodePtrArray = std::vector>; + +enum class ParamType { + Undef, + Scalar, + Vector, + FilamentVector, +}; + +// On all of 3 different platforms Bitmap+Text icon column looks different +// because of Markup text is missed or not implemented. +// As a temporary workaround, we will use: +// MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text) +// OSX - -//-, but Markup text is not implemented right now +// GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text) +class ParamsNode +{ + ParamsNode* m_parent{ nullptr }; + ParamsNodePtrArray m_children; + + ParamType m_param_type{ ParamType::Undef }; + + // TODO/FIXME: + // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded) + // needs to know in advance if a node is or _will be_ a container. + // Thus implementing: + // bool IsContainer() const + // { return m_children.size()>0; } + // doesn't work with wxGTK when DiffModel::AddToClassical is called + // AND the classical node was removed (a new node temporary without children + // would be added to the control) + bool m_container{ true }; + +public: + +#ifdef __linux__ + wxIcon icon; +#else + wxBitmap icon; +#endif //__linux__ + std::string icon_name; + std::string param_key; + wxString text; + + // Group params(root) node + ParamsNode(const wxString& group_name, const std::string& icon_name); + + // sub SlicingState node + ParamsNode(ParamsNode* parent, + const wxString& sub_group_name, + const std::string& icon_name); + + // parametre node + ParamsNode( ParamsNode* parent, + ParamType param_type, + const std::string& param_key); + + bool IsContainer() const { return m_container; } + bool IsGroupNode() const { return m_parent == nullptr; } + bool IsParamNode() const { return m_param_type != ParamType::Undef; } + void SetContainer(bool is_container) { m_container = is_container; } + + ParamsNode* GetParent() { return m_parent; } + ParamsNodePtrArray& GetChildren() { return m_children; } + + void Append(std::unique_ptr child) { m_children.emplace_back(std::move(child)); } +}; + + +// ---------------------------------------------------------------------------- +// ParamsModel +// ---------------------------------------------------------------------------- + +class ParamsModel : public wxDataViewModel +{ + ParamsNodePtrArray m_group_nodes; + wxDataViewCtrl* m_ctrl{ nullptr }; + +public: + + ParamsModel(); + ~ParamsModel() override = default; + + void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } + + wxDataViewItem AppendGroup(const wxString& group_name, + const std::string& icon_name); + + wxDataViewItem AppendSubGroup(wxDataViewItem parent, + const wxString& sub_group_name, + const std::string&icon_name); + + wxDataViewItem AppendParam( wxDataViewItem parent, + ParamType param_type, + const std::string& param_key); + + wxDataViewItem Delete(const wxDataViewItem& item); + + wxString GetParamName(wxDataViewItem item); + std::string GetParamKey(wxDataViewItem item); + + void Clear(); + + wxDataViewItem GetParent(const wxDataViewItem& item) const override; + unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + + void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; + bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + + bool IsContainer(const wxDataViewItem& item) const override; + // Is the container just a header or an item with all columns + // In our case it is an item with all columns + bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } +}; + + +// ---------------------------------------------------------------------------- +// ParamsViewCtrl +// ---------------------------------------------------------------------------- + +class ParamsViewCtrl : public wxDataViewCtrl +{ + int m_em_unit; + +public: + ParamsViewCtrl(wxWindow* parent, wxSize size); + ~ParamsViewCtrl() override { + if (model) { + Clear(); + model->DecRef(); + } + } + + ParamsModel* model{ nullptr }; + + wxDataViewItem AppendGroup(const wxString& group_name, + const std::string& icon_name); + + wxDataViewItem AppendSubGroup(wxDataViewItem parent, + const wxString& sub_group_name, + const std::string&icon_name); + + wxDataViewItem AppendParam( wxDataViewItem parent, + ParamType param_type, + const std::string& param_key); + + wxString GetValue(wxDataViewItem item); + wxString GetSelectedValue(); + std::string GetSelectedParamKey(); + + void CheckAndDeleteIfEmpty(wxDataViewItem item); + + void Clear(); + void Rescale(int em = 0); + + void set_em_unit(int em) { m_em_unit = em; } +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index c478a44833..e82f835587 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -14,6 +14,8 @@ #include "format.hpp" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/enum_bitmask.hpp" +#include "libslic3r/GCode/Thumbnails.hpp" #include #include @@ -32,7 +34,7 @@ #define wxOSX false #endif -namespace Slic3r { namespace GUI { +namespace Slic3r :: GUI { wxString double_to_string(double const value, const int max_precision /*= 4*/) { @@ -64,16 +66,22 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/) return s; } -wxString get_thumbnails_string(const std::vector& values) +ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ext = "PNG") { - wxString ret_str; - for (size_t i = 0; i < values.size(); ++ i) { - const Vec2d& el = values[i]; - ret_str += wxString::Format((i == 0) ? "%ix%i" : ", %ix%i", int(el[0]), int(el[1])); - } - return ret_str; -} + std::string input_string = into_u8(str); + str.Clear(); + + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(input_string); + if (!thumbnails_list.empty()) { + const auto& extentions = ConfigOptionEnum::get_enum_names(); + for (const auto& [format, size] : thumbnails_list) + str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(format)]); + str.resize(str.Len() - 2); + } + + return errors; +} Field::~Field() { @@ -179,6 +187,12 @@ void Field::on_back_to_sys_value() m_back_to_sys_value(m_opt_id); } +void Field::on_edit_value() +{ + if (m_fn_edit_value) + m_fn_edit_value(m_opt_id); +} + wxString Field::get_tooltip_text(const wxString& default_string) { if (m_opt.tooltip.empty()) @@ -361,56 +375,35 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true } } - m_value = into_u8(str); - break; } - - case coPoints: { - std::vector out_values; - str.Replace(" ", wxEmptyString, true); - if (!str.IsEmpty()) { - bool invalid_val = false; - bool out_of_range_val = false; - wxStringTokenizer thumbnails(str, ","); - while (thumbnails.HasMoreTokens()) { - wxString token = thumbnails.GetNextToken(); - double x, y; - wxStringTokenizer thumbnail(token, "x"); - if (thumbnail.HasMoreTokens()) { - wxString x_str = thumbnail.GetNextToken(); - if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) { - wxString y_str = thumbnail.GetNextToken(); - if (y_str.ToDouble(&y) && !thumbnail.HasMoreTokens()) { - if (0 < x && x < 1000 && 0 < y && y < 1000) { - out_values.push_back(Vec2d(x, y)); - continue; - } - out_of_range_val = true; - break; - } - } + if (m_opt.opt_key == "thumbnails") { + wxString str_out = str; + ThumbnailErrors errors = validate_thumbnails_string(str_out); + if (errors != enum_bitmask()) { + set_value(str_out, true); + wxString error_str; + if (errors.has(ThumbnailError::InvalidVal)) + error_str += format_wxstr(_L("Invalid input format. Expected vector of dimensions in the following format: \"%1%\""), "XxYxEXT, XxYxEXT, ..."); + if (errors.has(ThumbnailError::OutOfRange)) { + if (!error_str.empty()) + error_str += "\n\n"; + error_str += _L("Input value is out of range"); } - invalid_val = true; - break; + if (errors.has(ThumbnailError::InvalidExt)) { + if (!error_str.empty()) + error_str += "\n\n"; + error_str += _L("Some input extention is invalid"); + } + show_error(m_parent, error_str); } - - if (out_of_range_val) { - wxString text_value; - if (!m_value.empty()) - text_value = get_thumbnails_string(boost::any_cast>(m_value)); - set_value(text_value, true); - show_error(m_parent, _L("Input value is out of range")); - } - else if (invalid_val) { - wxString text_value; - if (!m_value.empty()) - text_value = get_thumbnails_string(boost::any_cast>(m_value)); - set_value(text_value, true); - show_error(m_parent, format_wxstr(_L("Invalid input format. Expected vector of dimensions in the following format: \"%1%\""),"XxY, XxY, ..." )); + else if (str_out != str) { + str = str_out; + set_value(str, true); } } - m_value = out_values; - break; } + m_value = into_u8(str); + break; + } default: break; @@ -490,9 +483,6 @@ void TextCtrl::BUILD() { text_value = vec->get_at(m_opt_idx); break; } - case coPoints: - text_value = get_thumbnails_string(m_opt.get_default_value()->values); - break; default: break; } @@ -1668,7 +1658,7 @@ void SliderCtrl::BUILD() m_textctrl->SetFont(Slic3r::GUI::wxGetApp().normal_font()); m_textctrl->SetBackgroundStyle(wxBG_STYLE_PAINT); - temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0); + temp->Add(m_slider, 1, wxEXPAND, 0); temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) { @@ -1711,5 +1701,5 @@ boost::any& SliderCtrl::get_value() } -} // GUI -} // Slic3r +} // Slic3r :: GUI + diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index a5318fb9ed..bdc8199136 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -41,7 +41,6 @@ using t_change = std::function; wxString double_to_string(double const value, const int max_precision = 4); -wxString get_thumbnails_string(const std::vector& values); class UndoValueUIManager { @@ -99,6 +98,30 @@ class UndoValueUIManager UndoValueUI m_undo_ui; + struct EditValueUI { + // Bitmap and Tooltip text for m_Edit_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. + const ScalableBitmap* bitmap{ nullptr }; + wxString tooltip { wxEmptyString }; + + bool set_bitmap(const ScalableBitmap* bmp) { + if (bitmap != bmp) { + bitmap = bmp; + return true; + } + return false; + } + + bool set_tooltip(const wxString& tip) { + if (tooltip != tip) { + tooltip = tip; + return true; + } + return false; + } + }; + + EditValueUI m_edit_ui; + public: UndoValueUIManager() {} ~UndoValueUIManager() {} @@ -109,6 +132,9 @@ public: bool set_undo_tooltip(const wxString* tip) { return m_undo_ui.set_undo_tooltip(tip); } bool set_undo_to_sys_tooltip(const wxString* tip) { return m_undo_ui.set_undo_to_sys_tooltip(tip); } + bool set_edit_bitmap(const ScalableBitmap* bmp) { return m_edit_ui.set_bitmap(bmp); } + bool set_edit_tooltip(const wxString& tip) { return m_edit_ui.set_tooltip(tip); } + // ui items used for revert line value bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; } const wxBitmapBundle& undo_bitmap() const { return m_undo_ui.undo_bitmap->bmp(); } @@ -116,8 +142,17 @@ public: const wxBitmapBundle& undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap->bmp(); } const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; } const wxColour* label_color() const { return m_undo_ui.label_color; } + + // Extentions + + // Search blinker const bool blink() const { return m_undo_ui.blink; } bool* get_blink_ptr() { return &m_undo_ui.blink; } + + // Edit field button + bool has_edit_ui() const { return !m_edit_ui.tooltip.IsEmpty(); } + const wxBitmapBundle* edit_bitmap() const { return &m_edit_ui.bitmap->bmp(); } + const wxString* edit_tooltip() const { return &m_edit_ui.tooltip; } }; @@ -151,6 +186,8 @@ public: void on_back_to_initial_value(); /// Call the attached m_back_to_sys_value method. void on_back_to_sys_value(); + /// Call the attached m_fn_edit_value method. + void on_edit_value(); public: /// parent wx item, opportunity to refactor (probably not necessary - data duplication) @@ -166,6 +203,9 @@ public: t_back_to_init m_back_to_initial_value{ nullptr }; t_back_to_init m_back_to_sys_value{ nullptr }; + /// Callback function to edit field value + t_back_to_init m_fn_edit_value{ nullptr }; + // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_disable_change_event {false}; bool m_is_modified_value {false}; diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index c3828fe8f8..fa035abf66 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -845,7 +845,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : grid->Add(port_sizer, 0, wxEXPAND); grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL); - grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); + grid->Add(p->progressbar, 1, wxEXPAND); grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL); grid->Add(p->txt_status, 0, wxEXPAND); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index f31e2e65aa..a56ae818ec 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -368,72 +368,162 @@ void GCodeViewer::SequentialView::Marker::render() ImGui::PopStyleVar(); } -void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const GCodeProcessorResult& gcode_result) { - assert(! m_file.is_open()); - if (m_file.is_open()) - return; - - m_filename = filename; - m_lines_ends = lines_ends; - - m_selected_line_id = 0; - m_last_lines_size = 0; - - try - { - m_file.open(boost::filesystem::path(m_filename)); - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; - reset(); - } + m_filename = gcode_result.filename; + m_is_binary_file = gcode_result.is_binary_file; + m_lines_ends = gcode_result.lines_ends; } -void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const +void GCodeViewer::SequentialView::GCodeWindow::add_gcode_line_to_lines_cache(const std::string& src) { - auto update_lines = [this](uint64_t start_id, uint64_t end_id) { - std::vector ret; - ret.reserve(end_id - start_id + 1); - for (uint64_t id = start_id; id <= end_id; ++id) { - // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; - std::string gline(m_file.data() + start, len); + std::string command; + std::string parameters; + std::string comment; - std::string command; - std::string parameters; - std::string comment; + // extract comment + std::vector tokens; + boost::split(tokens, src, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; + m_lines_cache.push_back({ command, parameters, comment }); +} + +void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, size_t curr_line_id) +{ + auto update_lines_ascii = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); + const std::vector& lines_ends = m_lines_ends.front(); + FILE* file = boost::nowide::fopen(m_filename.c_str(), "rb"); + if (file != nullptr) { + for (size_t id = *m_cache_range.min; id <= *m_cache_range.max; ++id) { + assert(id > 0); + // read line from file + const size_t begin = id == 1 ? 0 : lines_ends[id - 2]; + const size_t len = lines_ends[id - 1] - begin; + std::string gline(len, '\0'); + fseek(file, begin, SEEK_SET); + const size_t rsize = fread((void*)gline.data(), 1, len, file); + if (ferror(file) || rsize != len) { + m_lines_cache.clear(); + break; + } + + add_gcode_line_to_lines_cache(gline); + } + fclose(file); + } + }; + + auto update_lines_binary = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); + + size_t cumulative_lines_count = 0; + std::vector cumulative_lines_counts; + cumulative_lines_counts.reserve(m_lines_ends.size()); + for (size_t i = 0; i < m_lines_ends.size(); ++i) { + cumulative_lines_count += m_lines_ends[i].size(); + cumulative_lines_counts.emplace_back(cumulative_lines_count); + } + + size_t first_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.min <= cumulative_lines_counts[i]) { + first_block_id = i; + break; + } + } + size_t last_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.max <= cumulative_lines_counts[i]) { + last_block_id = i; + break; + } + } + assert(last_block_id >= first_block_id); + + FilePtr file(boost::nowide::fopen(m_filename.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + if (res == EResult::Success) { + for (size_t i = 0; i < first_block_id; ++i) { + skip_block(*file.f, file_header, block_header); + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; + } + } + + for (size_t i = first_block_id; i <= last_block_id; ++i) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) { + m_lines_cache.clear(); + return; + } + + const size_t ref_id = (i == 0) ? 0 : i - 1; + const size_t first_line_id = (i == 0) ? *m_cache_range.min : + (*m_cache_range.min - 1 >= cumulative_lines_counts[ref_id]) ? *m_cache_range.min - cumulative_lines_counts[ref_id] : 1; + const size_t last_line_id = (*m_cache_range.max - 1 <= cumulative_lines_counts[i]) ? + (i == 0) ? *m_cache_range.max : *m_cache_range.max - cumulative_lines_counts[ref_id] : m_lines_ends[i].size() - 1; + + for (size_t j = first_line_id; j <= last_line_id; ++j) { + const size_t begin = (j == 1) ? 0 : m_lines_ends[i][j - 2]; + const size_t end = m_lines_ends[i][j - 1]; + std::string gline; + gline.insert(gline.end(), block.raw_data.begin() + begin, block.raw_data.begin() + end); + add_gcode_line_to_lines_cache(gline); + } + + if (ftell(file.f) == file_size) + break; + + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; + } } } } - ret.push_back({ command, parameters, comment }); } - return ret; }; - static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; - static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; - static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; - static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; - static const ImVec4 ELLIPSIS_COLOR = { 0.0f, 0.7f, 0.0f, 1.0f }; + static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; + static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; + static const ImVec4 ELLIPSIS_COLOR = { 0.0f, 0.7f, 0.0f, 1.0f }; if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) return; @@ -444,37 +534,46 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u // number of visible lines const float text_height = ImGui::CalcTextSize("0").y; const ImGuiStyle& style = ImGui::GetStyle(); - const uint64_t lines_count = static_cast((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y)); + const size_t visible_lines_count = static_cast((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y)); - if (lines_count == 0) + if (visible_lines_count == 0) return; + if (m_lines_ends.empty() || m_lines_ends.front().empty()) + return; + + auto resize_range = [&](Range& range, size_t lines_count) { + const size_t half_lines_count = lines_count / 2; + range.min = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; + range.max = *range.min + lines_count - 1; + size_t lines_ends_count = 0; + for (const auto& le : m_lines_ends) { + lines_ends_count += le.size(); + } + if (*range.max >= lines_ends_count) { + range.max = lines_ends_count - 1; + range.min = *range.max - lines_count + 1; + } + }; + // visible range - const uint64_t half_lines_count = lines_count / 2; - uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; - uint64_t end_id = start_id + lines_count - 1; - if (end_id >= static_cast(m_lines_ends.size())) { - end_id = static_cast(m_lines_ends.size()) - 1; - start_id = end_id - lines_count + 1; + Range visible_range; + resize_range(visible_range, visible_lines_count); + + // update cache if needed + if (m_cache_range.empty() || !m_cache_range.contains(visible_range)) { + resize_range(m_cache_range, 4 * visible_range.size()); + if (m_is_binary_file) + update_lines_binary(); + else + update_lines_ascii(); } - // updates list of lines to show, if needed - if (m_selected_line_id != curr_line_id || m_last_lines_size != end_id - start_id + 1) { - try - { - *const_cast*>(&m_lines) = update_lines(start_id, end_id); - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << m_filename << ". Cannot show G-code window."; - return; - } - *const_cast(&m_selected_line_id) = curr_line_id; - *const_cast(&m_last_lines_size) = m_lines.size(); - } + if (m_lines_cache.empty()) + return; // line number's column width - const float id_width = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; + const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.max).c_str()).x; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -494,14 +593,10 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u current_length += out_text.length(); ImGui::SameLine(0.0f, spacing); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(out_text); - ImGui::PopStyleColor(); + imgui.text_colored(color, out_text); if (reduced) { ImGui::SameLine(0.0f, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ELLIPSIS_COLOR); - imgui.text("..."); - ImGui::PopStyleColor(); + imgui.text_colored(ELLIPSIS_COLOR, "..."); } return reduced; @@ -512,14 +607,15 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::SetNextWindowBgAlpha(0.6f); imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - + // center the text in the window by pushing down the first line - const float f_lines_count = static_cast(lines_count); + const float f_lines_count = static_cast(visible_lines_count); ImGui::SetCursorPosY(0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y)); // render text lines - for (uint64_t id = start_id; id <= end_id; ++id) { - const Line& line = m_lines[id - start_id]; + size_t max_line_length = 0; + for (size_t id = *visible_range.min; id <= *visible_range.max; ++id) { + const Line& line = m_lines_cache[id - *m_cache_range.min]; // rect around the current selected line if (id == curr_line_id) { @@ -547,16 +643,18 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u if (!stop_adding && !line.comment.empty()) // render comment stop_adding = add_item_to_line(line.comment, COMMENT_COLOR, line.command.empty() ? -1.0f : 0.0f, line_length); + + max_line_length = std::max(max_line_length, line_length); } imgui.end(); ImGui::PopStyleVar(); -} -void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() -{ - if (m_file.is_open()) - m_file.close(); + // request an extra frame if window's width changed + if (m_max_line_length != max_line_length) { + m_max_line_length = max_line_length; + imgui.set_requires_extra_frame(); + } } void GCodeViewer::SequentialView::render(float legend_height) @@ -737,7 +835,7 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr // release gpu memory, if used reset(); - m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); + m_sequential_view.gcode_window.load_gcode(gcode_result); if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; @@ -2360,17 +2458,19 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::LayerTimeLinear: case EViewType::LayerTimeLogarithmic: { - const Path::Sub_Path& sub_path = path.sub_paths.front(); - double z = static_cast(sub_path.first.position.z()); - const std::vector& zs = m_layers.get_zs(); - const std::vector& ranges = m_layers.get_ranges(); - size_t time_mode_id = static_cast(m_time_estimate_mode); - for (size_t i = 0; i < zs.size(); ++i) { - if (std::abs(zs[i] - z) < EPSILON) { - if (ranges[i].contains(sub_path.first.s_id)) { - color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], - (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); - break; + if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { + const Path::Sub_Path& sub_path = path.sub_paths.front(); + double z = static_cast(sub_path.first.position.z()); + const std::vector& zs = m_layers.get_zs(); + const std::vector& ranges = m_layers.get_ranges(); + size_t time_mode_id = static_cast(m_time_estimate_mode); + for (size_t i = 0; i < zs.size(); ++i) { + if (std::abs(zs[i] - z) < EPSILON) { + if (ranges[i].contains(sub_path.first.s_id)) { + color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], + (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); + break; + } } } } @@ -3643,17 +3743,25 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f }); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f }); - imgui.combo(std::string(), { _u8L("Feature type"), - _u8L("Height (mm)"), - _u8L("Width (mm)"), - _u8L("Speed (mm/s)"), - _u8L("Fan speed (%)"), - _u8L("Temperature (°C)"), - _u8L("Volumetric flow rate (mm³/s)"), - _u8L("Layer time (linear)"), - _u8L("Layer time (logarithmic)"), - _u8L("Tool"), - _u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f); + std::vector view_options; + std::vector view_options_id; + if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { + view_options = { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), _u8L("Speed (mm/s)"), _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), _u8L("Volumetric flow rate (mm³/s)"), _u8L("Layer time (linear)"), _u8L("Layer time (logarithmic)"), + _u8L("Tool"), _u8L("Color Print") }; + view_options_id = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + } + else { + view_options = { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), _u8L("Speed (mm/s)"), _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), _u8L("Volumetric flow rate (mm³/s)"), _u8L("Tool"), _u8L("Color Print") }; + view_options_id = { 0, 1, 2, 3, 4, 5, 6, 9, 10 }; + if (view_type == 7 || view_type == 8) + view_type = 0; + } + auto view_type_it = std::find(view_options_id.begin(), view_options_id.end(), view_type); + int view_type_id = (view_type_it == view_options_id.end()) ? 0 : std::distance(view_options_id.begin(), view_type_it); + if (imgui.combo(std::string(), view_options, view_type_id, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f)) + view_type = view_options_id[view_type_id]; ImGui::PopStyleColor(2); if (old_view_type != view_type) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index aacc61a23d..c1e9ae874e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -12,8 +12,6 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" -#include - #include #include #include @@ -687,32 +685,43 @@ public: std::string parameters; std::string comment; }; + + struct Range + { + std::optional min; + std::optional max; + bool empty() const { + return !min.has_value() || !max.has_value(); + } + bool contains(const Range& other) const { + return !this->empty() && !other.empty() && *this->min <= *other.min && *this->max >= other.max; + } + size_t size() const { + return empty() ? 0 : *this->max - *this->min + 1; + } + }; + bool m_visible{ true }; - uint64_t m_selected_line_id{ 0 }; - size_t m_last_lines_size{ 0 }; std::string m_filename; - boost::iostreams::mapped_file_source m_file; + bool m_is_binary_file{ false }; // map for accessing data in file by line number - std::vector m_lines_ends; - // current visible lines - std::vector m_lines; + std::vector> m_lines_ends; + std::vector m_lines_cache; + Range m_cache_range; + size_t m_max_line_length{ 0 }; public: - GCodeWindow() = default; - ~GCodeWindow() { stop_mapping_file(); } - void load_gcode(const std::string& filename, const std::vector& lines_ends); + void load_gcode(const GCodeProcessorResult& gcode_result); void reset() { - stop_mapping_file(); m_lines_ends.clear(); - m_lines.clear(); + m_lines_cache.clear(); m_filename.clear(); } - void toggle_visibility() { m_visible = !m_visible; } + void render(float top, float bottom, size_t curr_line_id); - void render(float top, float bottom, uint64_t curr_line_id) const; - - void stop_mapping_file(); + private: + void add_gcode_line_to_lines_cache(const std::string& src); }; struct Endpoints @@ -821,12 +830,16 @@ public: const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; } const BoundingBoxf3& get_shells_bounding_box() const { return m_shells_bounding_box; } + const BoundingBoxf3& get_max_bounding_box() const { BoundingBoxf3& max_bounding_box = const_cast(m_max_bounding_box); if (!max_bounding_box.defined) { - max_bounding_box = m_shells_bounding_box; - max_bounding_box.merge(m_paths_bounding_box); - max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ()); + if (m_shells_bounding_box.defined) + max_bounding_box = m_shells_bounding_box; + if (m_paths_bounding_box.defined) { + max_bounding_box.merge(m_paths_bounding_box); + max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ()); + } } return m_max_bounding_box; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9ae0a94aeb..40c969aba0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2008,6 +2008,11 @@ void GLCanvas3D::render() #endif // ENABLE_SLA_VIEW_DEBUG_WINDOW } +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + if (wxGetApp().plater()->is_view3D_shown() && current_printer_technology() != ptSLA && fff_print()->config().gcode_binary) + show_binary_gcode_debug_window(); +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + std::string tooltip; // Negative coordinate means out of the window, likely because the window was deactivated. @@ -7781,6 +7786,96 @@ void GLCanvas3D::GizmoHighlighter::blink() invalidate(); } +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW +void GLCanvas3D::show_binary_gcode_debug_window() +{ + bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + using namespace bgcode::core; + if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "File metadata compression"); + ImGui::TableSetColumnIndex(1); + std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; + int option_id = (int)binarizer_config.compression.file_metadata; + if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.file_metadata = (ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Printer metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.printer_metadata; + if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.printer_metadata = (ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Print metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.print_metadata; + if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.print_metadata = (ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Slicer metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.slicer_metadata; + if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.slicer_metadata = (ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GCode compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.gcode; + if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.gcode = (ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GCode encoding"); + ImGui::TableSetColumnIndex(1); + options = { "None", "MeatPack", "MeatPack Comments" }; + option_id = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.gcode_encoding = (EGCodeEncodingType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); + ImGui::TableSetColumnIndex(1); + options = { "INI" }; + option_id = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.metadata_encoding = (EMetadataEncodingType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); + ImGui::TableSetColumnIndex(1); + options = { "None", "CRC32" }; + option_id = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.checksum = (EChecksumType)option_id; + + ImGui::EndTable(); + + ImGui::Separator(); + imgui.text("!!! WARNING !!!"); + imgui.text("Changing values does NOT invalidate the current slice"); + } + + imgui.end(); +} +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) { const ModelVolume * ret = nullptr; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 90001c26c7..26be98665b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1102,6 +1102,10 @@ private: bool _deactivate_arrange_menu(); float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } + +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + void show_binary_gcode_debug_window(); +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW }; const ModelVolume *get_model_volume(const GLVolume &v, const Model &model); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 2b5c12ba3f..a74fd7f3dd 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -344,7 +344,7 @@ void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_su }; for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) { - changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(substitution.preset_name)); + changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(from_u8(substitution.preset_name))); if (!substitution.preset_file.empty()) changes += format_wxstr(" (%1%)", substitution.preset_file); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a916f53e93..c50bd80853 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -488,7 +488,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_STEP */ { "STEP files"sv, { ".stp"sv, ".step"sv } }, /* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"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, ".bgcode"sv, ".bgc"sv, ".g"sv, ".ngc"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_FONTS */ { "Font files"sv, { ".ttc"sv, ".ttf"sv } }, @@ -930,8 +930,8 @@ void GUI_App::init_app_config() { // 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 "-alpha"); +// SetAppName(SLIC3R_APP_KEY); + SetAppName(SLIC3R_APP_KEY "-alpha"); // SetAppName(SLIC3R_APP_KEY "-beta"); @@ -1628,7 +1628,8 @@ void GUI_App::UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited/* = false*/) #ifdef _WIN32 UpdateDarkUI(dvc, highlited ? dark_mode() : false); #ifdef _MSW_DARK_MODE - dvc->RefreshHeaderDarkMode(&m_normal_font); + if (!dvc->HasFlag(wxDV_NO_HEADER)) + dvc->RefreshHeaderDarkMode(&m_normal_font); #endif //_MSW_DARK_MODE if (dvc->HasFlag(wxDV_ROW_LINES)) dvc->SetAlternateRowColour(m_color_highlight_default); @@ -2024,7 +2025,7 @@ void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const { input_file.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), - _L("Choose one file (GCODE/.GCO/.G/.ngc/NGC):"), + _L("Choose one file (GCODE/GCO/G/BGCODE/BGC/NGC):"), app_config->get_last_dir(), "", file_wildcards(FT_GCODE), wxFD_OPEN | wxFD_FILE_MUST_EXIST); @@ -3014,9 +3015,9 @@ wxString GUI_App::current_language_code_safe() const { "ja", "ja_JP", }, { "ko", "ko_KR", }, { "pl", "pl_PL", }, - { "uk", "uk_UA", }, - { "zh", "zh_CN", }, - { "ru", "ru_RU", }, + //{ "uk", "uk_UA", }, + //{ "zh", "zh_CN", }, + //{ "ru", "ru_RU", }, }; wxString language_code = this->current_language_code().BeforeFirst('_'); auto it = mapping.find(language_code); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index f409756f01..6f0079e3e5 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -4,6 +4,7 @@ ///|/ #include "GUI_Utils.hpp" #include "GUI_App.hpp" +#include "format.hpp" #include #include @@ -304,8 +305,10 @@ TaskTimer::~TaskTimer() { std::chrono::milliseconds stop_timer = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()); - auto process_duration = std::chrono::milliseconds(stop_timer - start_timer).count(); - std::string out = (boost::format("\n!!! %1% duration = %2% ms \n\n") % task_name % process_duration).str(); + const auto timer_delta = stop_timer - start_timer; + const auto process_duration_ms = std::chrono::milliseconds(timer_delta).count(); + const auto process_duration_s = std::chrono::duration_cast>(timer_delta).count(); + std::string out = format("\n! \"%1%\" duration = %2% s (%3% ms) \n", task_name, process_duration_s, process_duration_ms); printf("%s", out.c_str()); #ifdef __WXMSW__ std::wstring stemp = std::wstring(out.begin(), out.end()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 2fec29693f..e96e4b37a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -231,6 +231,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, {"Shape" , _u8L("Shape")}, {"Depth" , _u8L("Depth")}, {"Size" , _u8L("Size")}, + {"Rotation" , _u8L("Rotation")}, {"Groove" , _u8L("Groove")}, {"Width" , _u8L("Width")}, {"Flap Angle" , _u8L("Flap Angle")}, @@ -562,10 +563,10 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/) { - constexpr float UndefMinVal = -0.1f; + static constexpr const float UndefMinVal = -0.1f; const float f_mm_to_in = static_cast(ObjectManipulation::mm_to_in); - auto render_slider = [this, UndefMinVal, f_mm_to_in] + auto render_slider = [this, f_mm_to_in] (const std::string& label, float& val, float def_val, float max_val, const wxString& tooltip) { float min_val = val < 0.f ? UndefMinVal : def_val; float value = val; @@ -2329,6 +2330,11 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) connectors[idx].radius_tolerance = 0.5f * m_connector_size_tolerance; }); + if (render_angle_input(m_labels_map["Rotation"], m_connector_angle, 0.f, 0.f, 180.f)) + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].z_angle = m_connector_angle; + }); + if (m_connector_type == CutConnectorType::Snap) { render_snap_specific_input(_u8L("Bulge"), _L("Bulge proportion related to radius"), m_snap_bulge_proportion, 0.15f, 5.f, 100.f * m_snap_space_proportion); render_snap_specific_input(_u8L("Space"), _L("Space proportion related to radius"), m_snap_space_proportion, 0.3f, 10.f, 50.f); @@ -2538,7 +2544,7 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in } } -void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +bool GLGizmoCut3D::render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) { bool is_changed{ false }; @@ -2551,14 +2557,15 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in const float old_val = val; const std::string format = "%.0f " + _u8L("°"); - m_imgui->slider_float(("##groove_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); + m_imgui->slider_float(("##angle_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); m_is_slider_editing_done |= m_imgui->get_last_slider_status().deactivated_after_edit; if (!is_approx(old_val, val)) { if (m_imgui->get_last_slider_status().can_take_snapshot) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Edited"), label), UndoRedo::SnapshotType::GizmoAction); m_imgui->get_last_slider_status().invalidate_snapshot(); - m_groove_editing = true; + if (m_mode == size_t(CutMode::cutTongueAndGroove)) + m_groove_editing = true; } in_val = deg2rad(val); is_changed = true; @@ -2568,14 +2575,19 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->disabled_begin(is_approx(in_val, init_val)); const std::string act_name = _u8L("Reset"); - if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + if (render_reset_button(("##angle_" + label + act_name).c_str(), act_name)) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); in_val = init_val; is_changed = true; } m_imgui->disabled_end(); - if (is_changed) { + return is_changed; +} + +void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +{ + if (render_angle_input(label, in_val, init_val, min_val, max_val)) { update_plane_model(); reset_cut_by_contours(); } @@ -2793,6 +2805,8 @@ void GLGizmoCut3D::validate_connector_settings() m_connector_size = 2.5f; if (m_connector_size_tolerance < 0.f) m_connector_size_tolerance = 0.f; + if (m_connector_angle < 0.f || m_connector_angle > float(PI) ) + m_connector_angle = 0.f; if (m_connector_type == CutConnectorType::Undef) m_connector_type = CutConnectorType::Plug; @@ -2812,6 +2826,7 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) float depth_ratio_tolerance { UndefFloat }; float radius { UndefFloat }; float radius_tolerance { UndefFloat }; + float angle { UndefFloat }; CutConnectorType type { CutConnectorType::Undef }; CutConnectorStyle style { CutConnectorStyle::Undef }; CutConnectorShape shape { CutConnectorShape::Undef }; @@ -2825,6 +2840,7 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) depth_ratio_tolerance = connector.height_tolerance; radius = connector.radius; radius_tolerance = connector.radius_tolerance; + angle = connector.z_angle; type = connector.attribs.type; style = connector.attribs.style; shape = connector.attribs.shape; @@ -2842,6 +2858,8 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) radius = UndefFloat; if (!is_approx(radius_tolerance, connector.radius_tolerance)) radius_tolerance = UndefFloat; + if (!is_approx(angle, connector.z_angle)) + angle = UndefFloat; if (type != connector.attribs.type) type = CutConnectorType::Undef; @@ -2857,6 +2875,7 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) m_connector_size = 2.f * radius; m_connector_size_tolerance = 2.f * radius_tolerance; m_connector_type = type; + m_connector_angle = angle; m_connector_style = int(style); m_connector_shape_id = int(shape); } @@ -3023,7 +3042,7 @@ void GLGizmoCut3D::toggle_model_objects_visibility() { bool has_active_volume = false; std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - for (const auto raycaster : *raycasters) + for (const std::shared_ptr &raycaster : *raycasters) if (raycaster->is_active()) { has_active_volume = true; break; @@ -3108,7 +3127,8 @@ void GLGizmoCut3D::render_connectors() else if (!looking_forward) pos += 0.05 * m_clp_normal; - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * + rotation_transform(-connector.z_angle * Vec3d::UnitZ()) * scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); render_model(m_shapes[connector.attribs].model, render_color, view_model_matrix); @@ -3511,6 +3531,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p connectors.emplace_back(pos, m_rotation_m, m_connector_size * 0.5f, m_connector_depth_ratio, m_connector_size_tolerance * 0.5f, m_connector_depth_ratio_tolerance, + m_connector_angle, CutConnectorAttributes( CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); @@ -3735,6 +3756,7 @@ void GLGizmoCut3D::apply_cut_connectors(ModelObject* mo, const std::string& conn // Transform the new modifier to be aligned inside the instance new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m * + rotation_transform(-connector.z_angle * Vec3d::UnitZ()) * scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index dc4b784b79..89c1b92833 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -138,6 +138,7 @@ class GLGizmoCut3D : public GLGizmoBase float m_connector_depth_ratio{ 3.f }; float m_connector_size{ 2.5f }; + float m_connector_angle{ 0.f }; float m_connector_depth_ratio_tolerance{ 0.1f }; float m_connector_size_tolerance{ 0.f }; @@ -306,6 +307,7 @@ protected: void render_color_marker(float size, const ImU32& color); void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance); void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val); + bool render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val); void render_snap_specific_input(const std::string& label, const wxString& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val); void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 998a45f83e..169887740d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -791,7 +791,8 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& bool ImGuiWrapper::combo(const std::string& label, const std::vector& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/) { // this is to force the label to the left of the widget: - if (!label.empty()) { + const bool hidden_label = boost::starts_with(label, "##"); + if (!label.empty() && !hidden_label) { text(label); ImGui::SameLine(label_width); } @@ -801,7 +802,7 @@ bool ImGuiWrapper::combo(const std::string& label, const std::vector= 0 ? options[selection].c_str() : ""; - if (ImGui::BeginCombo(("##" + label).c_str(), selection_str, flags)) { + if (ImGui::BeginCombo(hidden_label ? label.c_str() : ("##" + label).c_str(), selection_str, flags)) { for (int i = 0; i < (int)options.size(); i++) { if (ImGui::Selectable(options[i].c_str(), i == selection)) { selection_out = i; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6cbb5f7722..22a1461812 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1370,6 +1370,15 @@ void MainFrame::init_menubar_as_editor() []() {return true; }, this); append_submenu(fileMenu, export_menu, wxID_ANY, _L("&Export"), ""); + wxMenu* convert_menu = new wxMenu(); + append_menu_item(convert_menu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + []() { return true; }, this); + append_menu_item(convert_menu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + []() { return true; }, this); + append_submenu(fileMenu, convert_menu, wxID_ANY, _L("&Convert"), ""); + append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD Card / Flash Drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."), [this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr, [this]() {return can_eject(); }, this); @@ -1643,12 +1652,19 @@ void MainFrame::init_menubar_as_gcodeviewer() "", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this); #endif // __APPLE__ fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + []() { return true; }, this); + append_menu_item(fileMenu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + []() { return true; }, this); + fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); append_menu_item(fileMenu, wxID_ANY, _L("Open &PrusaSlicer") + dots, _L("Open PrusaSlicer"), [](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, - []() {return true; }, this); + []() { return true; }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); @@ -1898,8 +1914,8 @@ void MainFrame::load_config_file() return; wxFileDialog dlg(this, _L("Select configuration to load:"), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), - "config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - wxString file; + "config.ini", "INI files (*.ini, *.gcode, *.bgcode)|*.ini;*.INI;*.gcode;*.g;*.bgcode;*.bgc", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + wxString file; if (dlg.ShowModal() == wxID_OK) file = dlg.GetPath(); if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 648ae6ec97..ad38d68435 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -282,6 +282,11 @@ void OG_CustomCtrl::OnMotion(wxMouseEvent& event) tooltip = *field->undo_to_sys_tooltip(); break; } + if (opt_idx < line.rects_edit_icon.size() && is_point_in_rect(pos, line.rects_edit_icon[opt_idx])) { + if (Field* field = opt_group->get_field(opt_key); field && field->has_edit_ui()) + tooltip = *field->edit_tooltip(); + break; + } } if (!tooltip.IsEmpty()) break; @@ -331,6 +336,13 @@ void OG_CustomCtrl::OnLeftDown(wxMouseEvent& event) event.Skip(); return; } + + if (opt_idx < line.rects_edit_icon.size() && is_point_in_rect(pos, line.rects_edit_icon[opt_idx])) { + if (Field* field = opt_group->get_field(opt_key)) + field->on_edit_value(); + event.Skip(); + return; + } } } @@ -569,17 +581,21 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) return; } + wxCoord h_pos = draw_mode_bmp(dc, v_pos); + Field* field = ctrl->opt_group->get_field(og_line.get_options().front().opt_id); const bool suppress_hyperlinks = get_app_config()->get_bool("suppress_hyperlinks"); if (draw_just_act_buttons) { - if (field) - draw_act_bmps(dc, wxPoint(0, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()); + if (field) { + const wxPoint pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()); + // Add edit button, if it exists + if (field->has_edit_ui()) + draw_edit_bmp(dc, pos, field->edit_bitmap()); + } return; } - wxCoord h_pos = draw_mode_bmp(dc, v_pos); - if (og_line.near_label_widget_win) h_pos += og_line.near_label_widget_win->GetSize().x + ctrl->m_h_gap; @@ -610,7 +626,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) option_set.front().side_widget == nullptr && og_line.get_extra_widgets().size() == 0) { if (field && field->has_undo_ui()) - h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()) + ctrl->m_h_gap; + h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()).x + ctrl->m_h_gap; else if (field && !field->has_undo_ui() && field->blink()) draw_blinking_bmp(dc, wxPoint(h_pos, v_pos), field->blink()); // update width for full_width fields @@ -639,7 +655,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) } if (field && field->has_undo_ui()) { - h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink(), bmp_rect_id++); + h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink(), bmp_rect_id++).x; if (field->getSizer()) { auto children = field->getSizer()->GetChildren(); @@ -750,7 +766,7 @@ wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool i return wxPoint(h_pos, v_pos); } -wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) +wxPoint OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) { pos = draw_blinking_bmp(dc, pos, is_blinking); wxCoord h_pos = pos.x; @@ -769,7 +785,19 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBi h_pos += bmp_dim + ctrl->m_h_gap; - return h_pos; + return wxPoint(h_pos, v_pos); +} + +wxCoord OG_CustomCtrl::CtrlLine::draw_edit_bmp(wxDC &dc, wxPoint pos, const wxBitmapBundle *bmp_edit) +{ + const wxCoord h_pos = pos.x + ctrl->m_h_gap; + const wxCoord v_pos = pos.y; + const int bmp_w = get_bitmap_size(bmp_edit, ctrl).GetWidth(); + rects_edit_icon.emplace_back(wxRect(h_pos, v_pos, bmp_w, bmp_w)); + + dc.DrawBitmap(bmp_edit->GetBitmapFor(ctrl), h_pos, v_pos); + + return h_pos + bmp_w + ctrl->m_h_gap; } bool OG_CustomCtrl::CtrlLine::launch_browser() const diff --git a/src/slic3r/GUI/OG_CustomCtrl.hpp b/src/slic3r/GUI/OG_CustomCtrl.hpp index 327468a4d2..374d3a5ffe 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.hpp +++ b/src/slic3r/GUI/OG_CustomCtrl.hpp @@ -66,12 +66,14 @@ class OG_CustomCtrl :public wxPanel wxCoord draw_mode_bmp(wxDC& dc, wxCoord v_pos); wxCoord draw_text (wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url = false); wxPoint draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking); - wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id = 0); + wxPoint draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id = 0); + wxCoord draw_edit_bmp(wxDC& dc, wxPoint pos, const wxBitmapBundle* bmp_edit); bool launch_browser() const; bool is_separator() const { return og_line.is_separator(); } std::vector rects_undo_icon; std::vector rects_undo_to_sys_icon; + std::vector rects_edit_icon; wxRect rect_label; }; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 0eeb8612ec..1e1d3c08bd 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -108,6 +108,14 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co }; field->m_parent = parent(); + if (edit_custom_gcode && opt.is_code) { + field->m_fn_edit_value = [this](std::string opt_id) { + if (!m_disabled) + this->edit_custom_gcode(opt_id); + }; + field->set_edit_tooltip(_L("Edit CustomG-code")); + } + field->m_back_to_initial_value = [this](std::string opt_id) { if (!m_disabled) this->back_to_initial_value(opt_id); @@ -916,9 +924,10 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config ret = double_to_string(val); } break; - case coString: - ret = from_u8(config.opt_string(opt_key)); - break; + case coString: { + ret = from_u8(config.opt_string(opt_key)); + break; + } case coStrings: if (opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "gcode_substitutions") { ret = config.option(opt_key)->values; @@ -958,8 +967,6 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config case coPoints: if (opt_key == "bed_shape") ret = config.option(opt_key)->values; - else if (opt_key == "thumbnails") - ret = get_thumbnails_string(config.option(opt_key)->values); else ret = config.option(opt_key)->get_at(idx); break; @@ -1014,7 +1021,7 @@ wxString OptionsGroup::get_url(const std::string& path_end) if (path_end.empty()) return wxEmptyString; - wxString language = get_app_config()->get("translation_language"); + wxString language = wxGetApp().current_language_code_safe(); wxString lang_marker = language.IsEmpty() ? "en" : language.BeforeFirst('_'); return wxString("https://help.prusa3d.com/") + lang_marker + wxString("/article/" + path_end); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 3a0c4d2440..ae500c5d32 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -132,6 +132,8 @@ public: std::function rescale_extra_column_item { nullptr }; std::function rescale_near_label_widget { nullptr }; + + std::function edit_custom_gcode { nullptr }; wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ebdba2f255..47c509e90a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,8 @@ #include #endif +#include + #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" @@ -2523,6 +2526,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ int answer_convert_from_meters = wxOK_DEFAULT; int answer_convert_from_imperial_units = wxOK_DEFAULT; + int answer_consider_as_multi_part_objects = wxOK_DEFAULT; bool in_temp = false; const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); @@ -2704,14 +2708,21 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (model.looks_like_multipart_object()) { - MessageDialog msg_dlg(q, _L( - "This file contains several objects positioned at multiple heights.\n" - "Instead of considering them as multiple objects, should \n" - "the file be loaded as a single object having multiple parts?") + "\n", - _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO); - if (msg_dlg.ShowModal() == wxID_YES) { - model.convert_multipart_object(nozzle_dmrs->values.size()); + if (answer_consider_as_multi_part_objects == wxOK_DEFAULT) { + RichMessageDialog dlg(q, _L( + "This file contains several objects positioned at multiple heights.\n" + "Instead of considering them as multiple objects, should \n" + "the file be loaded as a single object having multiple parts?") + "\n", + _L("Multi-part object detected"), wxICON_QUESTION | wxYES_NO); + dlg.ShowCheckBox(_L("Apply to all multiple objects being loaded.")); + int answer = dlg.ShowModal(); + if (dlg.IsCheckBoxChecked()) + answer_consider_as_multi_part_objects = answer; + if (answer == wxID_YES) + model.convert_multipart_object(nozzle_dmrs->size()); } + else if (answer_consider_as_multi_part_objects == wxID_YES) + model.convert_multipart_object(nozzle_dmrs->size()); } } if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { @@ -5494,6 +5505,109 @@ void Plater::reload_gcode_from_disk() load_gcode(filename); } +void Plater::convert_gcode_to_ascii() +{ + // Ask user for a gcode file name. + wxString input_file; + wxGetApp().load_gcode(this, input_file); + if (input_file.empty()) + return; + + // Open source file + FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; + if (in_file.f == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + + // Set out filename + boost::filesystem::path path(into_u8(input_file)); + const std::string output_file = path.replace_extension("gcode").string(); + if (boost::filesystem::exists(output_file)) { + MessageDialog msg_dlg(this, GUI::format_wxstr(_L("File %1% already exists. Do you wish to overwrite it?"), output_file), _L("Notice"), wxYES_NO); + if (msg_dlg.ShowModal() != wxID_YES) + return; + } + + // Open destination file + FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; + if (out_file.f == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + + // Perform conversion + { + wxBusyCursor busy; + using namespace bgcode::core; + EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); + if (res != EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + msg_dlg.ShowModal(); + out_file.close(); + boost::nowide::remove(output_file.c_str()); + return; + } + } + + MessageDialog msg_dlg(this, _L("Succesfully created gcode ascii file \n") + output_file, _L("Convert gcode file to ascii format"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); +} + +void Plater::convert_gcode_to_binary() +{ + // Ask user for a gcode file name. + wxString input_file; + wxGetApp().load_gcode(this, input_file); + if (input_file.empty()) + return; + + // Open source file + FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; + if (in_file.f == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + + // Set out filename + boost::filesystem::path path(into_u8(input_file)); + const std::string output_file = path.replace_extension("bgcode").string(); + if (boost::filesystem::exists(output_file)) { + MessageDialog msg_dlg(this, GUI::format_wxstr(_L("File %1% already exists. Do you wish to overwrite it?"), output_file), _L("Notice"), wxYES_NO); + if (msg_dlg.ShowModal() != wxID_YES) + return; + } + + // Open destination file + FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; + if (out_file.f == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + + // Perform conversion + { + wxBusyCursor busy; + using namespace bgcode::core; + const bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); + if (res != EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + msg_dlg.ShowModal(); + out_file.close(); + boost::nowide::remove(output_file.c_str()); + return; + } + } + + MessageDialog msg_dlg(this, _L("Succesfully created gcode binary file \n") + output_file, _L("Convert gcode file to binary format"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); +} + void Plater::refresh_print() { p->preview->refresh_print(); @@ -6001,7 +6115,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect) bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*=false*/) { const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip)", std::regex::icase); - const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase); + const std::regex pattern_gcode_drop(".*[.](gcode|g|bgcode|bgc)", std::regex::icase); std::vector paths; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f4d18a2916..6ded1c4a2f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -43,8 +43,6 @@ namespace Slic3r { class BuildVolume; class Model; class ModelObject; -enum class ModelObjectCutAttribute : int; -using ModelObjectCutAttributes = enum_bitmask; class ModelInstance; class Print; class SLAPrint; @@ -188,6 +186,8 @@ public: void load_gcode(); void load_gcode(const wxString& filename); void reload_gcode_from_disk(); + void convert_gcode_to_ascii(); + void convert_gcode_to_binary(); void refresh_print(); std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 82d724c9b7..e31e47647b 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -790,7 +790,7 @@ void PlaterPresetComboBox::show_edit_menu() [this](wxCommandEvent&) { this->change_extruder_color(); }, "funnel", menu, []() { return true; }, wxGetApp().plater()); #endif //__linux__ append_menu_item(menu, wxID_ANY, _L("Show/Hide template presets"), "", - [this](wxCommandEvent&) { wxGetApp().open_preferences("no_templates", "General"); }, "spool", menu, []() { return true; }, wxGetApp().plater()); + [](wxCommandEvent&) { wxGetApp().open_preferences("no_templates", "General"); }, "spool", menu, []() { return true; }, wxGetApp().plater()); wxGetApp().plater()->PopupMenu(menu); return; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ec8ae1139c..cc92730b74 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -26,7 +26,8 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" -#include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/GCode/GCodeWriter.hpp" +#include "libslic3r/GCode/Thumbnails.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/PrintHost.hpp" @@ -64,6 +65,7 @@ #include "format.hpp" #include "UnsavedChangesDialog.hpp" #include "SavePresetDialog.hpp" +#include "EditGCodeDialog.hpp" #include "MsgDialog.hpp" #include "Notebook.hpp" @@ -207,6 +209,8 @@ void Tab::create_preset_tab() // Bitmaps to be shown on the "Undo user changes" button next to each input field. add_scaled_bitmap(this, m_bmp_value_revert, "undo"); add_scaled_bitmap(this, m_bmp_white_bullet, "dot"); + // Bitmap to be shown on the "edit" button before to each editable input field. + add_scaled_bitmap(this, m_bmp_edit_value, "edit"); fill_icon_descriptions(); set_tooltips_text(); @@ -236,7 +240,7 @@ void Tab::create_preset_tab() const float scale_factor = em_unit(this)*0.1;// GetContentScaleFactor(); m_top_hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_top_hsizer, 0, wxEXPAND | wxBOTTOM | wxALIGN_CENTER_VERTICAL, 3); + sizer->Add(m_top_hsizer, 0, wxEXPAND | wxBOTTOM, 3); m_top_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); m_top_hsizer->AddSpacer(int(4*scale_factor)); @@ -262,7 +266,7 @@ void Tab::create_preset_tab() m_h_buttons_sizer->AddSpacer(int(8*scale_factor)); m_h_buttons_sizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL); - m_top_hsizer->Add(m_h_buttons_sizer, 1, wxEXPAND | wxALIGN_CENTRE_VERTICAL); + m_top_hsizer->Add(m_h_buttons_sizer, 1, wxEXPAND); m_top_hsizer->AddSpacer(int(16*scale_factor)); // StretchSpacer has a strange behavior under OSX, so // There is used just additional sizer for m_mode_sizer with right alignment @@ -620,6 +624,10 @@ void Tab::decorate() field->set_undo_tooltip(tt); field->set_undo_to_sys_tooltip(sys_tt); field->set_label_colour(color); + + if (field->has_edit_ui()) + field->set_edit_bitmap(&m_bmp_edit_value); + } if (m_active_page) @@ -1668,6 +1676,8 @@ void TabPrint::build() optgroup->append_single_option_line("slicing_mode"); optgroup->append_single_option_line("resolution"); optgroup->append_single_option_line("gcode_resolution"); + optgroup->append_single_option_line("arc_fitting"); + optgroup->append_single_option_line("arc_fitting_tolerance"); optgroup->append_single_option_line("xy_size_compensation"); optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487"); @@ -1693,6 +1703,34 @@ void TabPrint::build() Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); + optgroup->append_single_option_line("gcode_binary"); + + optgroup->m_on_change = [this](const t_config_option_key& opt_key, boost::any value) + { + if (opt_key == "gcode_binary") { + const bool is_binary = m_config->opt_bool("gcode_binary"); + std::string output_filename_format = m_config->opt_string("output_filename_format"); + bool modified = false; + if (is_binary && boost::iends_with(output_filename_format, ".gcode")) { + output_filename_format = output_filename_format.substr(0, output_filename_format.length() - 5) + "bgcode"; + modified = true; + } + else if (!is_binary && boost::iends_with(output_filename_format, ".bgcode")) { + output_filename_format = output_filename_format.substr(0, output_filename_format.length() - 6) + "gcode"; + modified = true; + } + if (modified) { + DynamicPrintConfig new_conf = *m_config; + auto off_option = static_cast(m_config->option("output_filename_format")->clone()); + off_option->value = output_filename_format; + new_conf.set_key_value("output_filename_format", off_option); + load_config(new_conf); + } + } + + update_dirty(); + update(); + }; optgroup = page->new_optgroup(L("Other")); @@ -1854,6 +1892,43 @@ static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_co tab->on_value_change(opt_key, value); } +void Tab::edit_custom_gcode(const t_config_option_key& opt_key) +{ + EditGCodeDialog dlg = EditGCodeDialog(this, opt_key, get_custom_gcode(opt_key)); + if (dlg.ShowModal() == wxID_OK) { + set_custom_gcode(opt_key, dlg.get_edited_gcode()); + update_dirty(); + update(); + } +} + +const std::string& Tab::get_custom_gcode(const t_config_option_key& opt_key) +{ + return m_config->opt_string(opt_key); +} + +void Tab::set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) +{ + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value(opt_key, new ConfigOptionString(value)); + load_config(new_conf); +} + +const std::string& TabFilament::get_custom_gcode(const t_config_option_key& opt_key) +{ + return m_config->opt_string(opt_key, unsigned(0)); +} + +void TabFilament::set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) +{ + std::vector gcodes = static_cast(m_config->option(opt_key))->values; + gcodes[0] = value; + + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value(opt_key, new ConfigOptionStrings(gcodes)); + load_config(new_conf); +} + void TabFilament::create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/) { Line line {"",""}; @@ -2189,6 +2264,7 @@ void TabFilament::build() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("start_filament_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2199,6 +2275,7 @@ void TabFilament::build() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("end_filament_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2365,7 +2442,7 @@ void TabFilament::load_current_preset() m_extruders_cb->Select(m_active_extruder); } - assert(m_active_extruder >= 0 && m_active_extruder < m_preset_bundle->extruders_filaments.size()); + assert(m_active_extruder >= 0 && size_t(m_active_extruder) < m_preset_bundle->extruders_filaments.size()); const std::string& selected_extr_filament_name = m_preset_bundle->extruders_filaments[m_active_extruder].get_selected_preset_name(); if (selected_extr_filament_name != selected_filament_name) { m_presets->select_preset_by_name(selected_extr_filament_name, false); @@ -2576,13 +2653,38 @@ void TabPrinter::build_fff() option = optgroup->get_option("thumbnails"); option.opt.full_width = true; optgroup->append_single_option_line(option); - optgroup->append_single_option_line("thumbnails_format"); optgroup->append_single_option_line("silent_mode"); optgroup->append_single_option_line("remaining_times"); optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { wxTheApp->CallAfter([this, opt_key, value]() { + if (opt_key == "thumbnails" && m_config->has("thumbnails_format")) { + // to backward compatibility we need to update "thumbnails_format" from new "thumbnails" + if (const std::string val = boost::any_cast(value); !value.empty()) { + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(val); + + if (errors != enum_bitmask()) { + std::string error_str = format(_u8L("Invalid value provided for parameter %1%: %2%"), "thumbnails", val); + error_str += GCodeThumbnails::get_error_string(errors); + InfoDialog(parent(), _L("G-code flavor is switched"), from_u8(error_str)).ShowModal(); + } + + if (!thumbnails_list.empty()) { + GCodeThumbnailsFormat old_format = GCodeThumbnailsFormat(m_config->option("thumbnails_format")->getInt()); + GCodeThumbnailsFormat new_format = thumbnails_list.begin()->first; + if (old_format != new_format) { + DynamicPrintConfig new_conf = *m_config; + + auto* opt = m_config->option("thumbnails_format")->clone(); + opt->setInt(int(new_format)); + new_conf.set_key_value("thumbnails_format", opt); + + load_config(new_conf); + } + } + } + } if (opt_key == "silent_mode") { bool val = boost::any_cast(value); if (m_use_silent_mode != val) { @@ -2647,6 +2749,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("start_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2660,6 +2763,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("end_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2670,6 +2774,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("before_layer_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2680,6 +2785,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("layer_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2690,6 +2796,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("toolchange_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2700,6 +2807,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("between_objects_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2710,6 +2818,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("color_change_gcode"); option.opt.is_code = true; option.opt.height = gcode_field_height;//150; @@ -2719,6 +2828,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("pause_print_gcode"); option.opt.is_code = true; option.opt.height = gcode_field_height;//150; @@ -2728,6 +2838,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("template_custom_gcode"); option.opt.is_code = true; option.opt.height = gcode_field_height;//150; @@ -4868,6 +4979,13 @@ void Tab::fill_icon_descriptions() "the current option group.\n" "Click the BACK ARROW icon to reset all settings for the current option group to " "the last saved preset.")); + + m_icon_descriptions.emplace_back(&m_bmp_edit_value, L("EDIT VALUE"), + // TRN Description for "EDIT VALUE" + L("indicates that the settings were changed and are not equal to the last saved preset for " + "the current option group.\n" + "Click the BACK ARROW icon to reset all settings for the current option group to " + "the last saved preset.")); } void Tab::set_tooltips_text() diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index ba59b0cde3..0cca2cd339 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -227,6 +227,8 @@ protected: ScalableBitmap *m_bmp_non_system; // Bitmaps to be shown on the "Undo user changes" button next to each input field. ScalableBitmap m_bmp_value_revert; + // Bitmaps to be shown on the "Undo user changes" button next to each input field. + ScalableBitmap m_bmp_edit_value; std::vector m_scaled_buttons = {}; std::vector m_scaled_bitmaps = {}; @@ -403,6 +405,10 @@ public: bool validate_custom_gcodes(); bool validate_custom_gcodes_was_shown{ false }; + void edit_custom_gcode(const t_config_option_key& opt_key); + virtual const std::string& get_custom_gcode(const t_config_option_key& opt_key); + virtual void set_custom_gcode(const t_config_option_key& opt_key, const std::string& value); + protected: void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const std::string& path, widget_t widget); wxSizer* compatible_widget_create(wxWindow* parent, PresetDependencies &deps); @@ -487,6 +493,9 @@ public: void update_extruder_combobox(); int get_active_extruder() const { return m_active_extruder; } + const std::string& get_custom_gcode(const t_config_option_key& opt_key) override; + void set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) override; + protected: bool select_preset_by_name(const std::string& name_w_suffix, bool force) override; bool save_current_preset(const std::string& new_name, bool detach) override; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 358021df9a..5ae26ea9e8 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1210,8 +1210,6 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& BedShape shape(*config.option(opt_key)); return shape.get_full_name_with_params(); } - if (opt_key == "thumbnails") - return get_thumbnails_string(config.option(opt_key)->values); Vec2d val = config.opt(opt_key)->get_at(opt_idx); return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str()); diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index a8324dab26..6671b68d8a 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -357,7 +357,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con hsizer->AddSpacer(10); hsizer->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - gridsizer_simple->Add(hsizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); + gridsizer_simple->Add(hsizer, 1, wxEXPAND); gridsizer_simple->Add(m_old.back(),0); gridsizer_simple->Add(m_new.back(),0); } diff --git a/t/gcode.t b/t/gcode.t index 902c40b834..7bf6b06535 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 23; +use Test::More tests => 22; use strict; use warnings; @@ -185,13 +185,14 @@ use Slic3r::Test; } } -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('start_gcode', 'START:[input_filename]'); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code'; -} +#{ +# [input_filename] placeholder was removed in 0cbbe96. +# my $config = Slic3r::Config::new_from_defaults; +# $config->set('start_gcode', 'START:[input_filename]'); +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# my $gcode = Slic3r::Test::gcode($print); +# like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code'; +#} # The current Spiral Vase slicing code removes the holes and all but the largest contours from each slice, # therefore the following test is no more valid. diff --git a/t/geometry.t b/t/geometry.t deleted file mode 100644 index 83fb72eeab..0000000000 --- a/t/geometry.t +++ /dev/null @@ -1,95 +0,0 @@ -use Test::More; -use strict; -use warnings; - -plan tests => 10; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Slic3r; -use Slic3r::Geometry qw(PI - chained_path_from epsilon scale); - -{ - # this test was failing on Windows (GH #1950) - my $polygon = Slic3r::Polygon->new( - [207802834,-57084522],[196528149,-37556190],[173626821,-25420928],[171285751,-21366123], - [118673592,-21366123],[116332562,-25420928],[93431208,-37556191],[82156517,-57084523], - [129714478,-84542120],[160244873,-84542120], - ); - my $point = Slic3r::Point->new(95706562, -57294774); - ok $polygon->contains_point($point), 'contains_point'; -} - -#========================================================== - -my $polygons = [ - Slic3r::Polygon->new( # contour, ccw - [45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800], - [43749700, 343843000], [45422300, 352251500], [52362100, 362637800], [62748400, 369577600], - [75000000, 372014700], [87251500, 369577600], [97637800, 362637800], [104577600, 352251500], - [107014700, 340000000], [104577600, 327748400], [97637800, 317362100], [87251500, 310422300], - [82789200, 309534700], [69846100, 294726100], [254081000, 294726100], [285273900, 348753500], - [285273900, 461246400], [254081000, 515273900], - - ), - Slic3r::Polygon->new( # hole, cw - [75000000, 502014700], [87251500, 499577600], [97637800, 492637800], [104577600, 482251500], - [107014700, 470000000], [104577600, 457748400], [97637800, 447362100], [87251500, 440422300], - [75000000, 437985300], [62748400, 440422300], [52362100, 447362100], [45422300, 457748400], - [42985300, 470000000], [45422300, 482251500], [52362100, 492637800], [62748400, 499577600], - ), -]; - - -#========================================================== - -{ - my $polygon = Slic3r::Polygon->new([0, 0], [10, 0], [5, 5]); - my $result = $polygon->split_at_index(1); - is ref($result), 'Slic3r::Polyline', 'split_at_index returns polyline'; - is_deeply $result->pp, [ [10, 0], [5, 5], [0, 0], [10, 0] ], 'split_at_index'; -} - -#========================================================== - -#{ -# my $bb = Slic3r::Geometry::BoundingBox->new_from_points([ map Slic3r::Point->new(@$_), [0, 1], [10, 2], [20, 2] ]); -# $bb->scale(2); -# is_deeply [ $bb->min_point->pp, $bb->max_point->pp ], [ [0,2], [40,4] ], 'bounding box is scaled correctly'; -#} - -#========================================================== - -{ - # if chained_path() works correctly, these points should be joined with no diagonal paths - # (thus 26 units long) - my @points = map Slic3r::Point->new_scale(@$_), [26,26],[52,26],[0,26],[26,52],[26,0],[0,52],[52,52],[52,0]; - my @ordered = @points[@{chained_path_from(\@points, $points[0])}]; - ok !(grep { abs($ordered[$_]->distance_to($ordered[$_+1]) - scale 26) > epsilon } 0..$#ordered-1), 'chained_path'; -} - -#========================================================== - -{ - my $line = Slic3r::Line->new([0, 0], [20, 0]); - is +Slic3r::Point->new(10, 10)->distance_to_line($line), 10, 'distance_to'; - is +Slic3r::Point->new(50, 0)->distance_to_line($line), 30, 'distance_to'; - is +Slic3r::Point->new(0, 0)->distance_to_line($line), 0, 'distance_to'; - is +Slic3r::Point->new(20, 0)->distance_to_line($line), 0, 'distance_to'; - is +Slic3r::Point->new(10, 0)->distance_to_line($line), 0, 'distance_to'; -} - -{ - my $triangle = Slic3r::Polygon->new( - [16000170,26257364], [714223,461012], [31286371,461008], - ); - my $simplified = $triangle->simplify(250000)->[0]; - is scalar(@$simplified), 3, 'triangle is never simplified to less than 3 points'; -} - -__END__ diff --git a/t/layers.t b/t/layers.t index 4d958808a6..fef94457c2 100644 --- a/t/layers.t +++ b/t/layers.t @@ -62,6 +62,7 @@ use Slic3r::Test qw(_eq); { my $config = Slic3r::Config->new; $config->set('fill_density', 0); # just for making the test faster + $config->set('gcode_binary', 0); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2); my @z = (); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d6d16db1d2..af2a4e033d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) add_subdirectory(arrange) +add_subdirectory(thumbnails) add_subdirectory(libslic3r) add_subdirectory(slic3rutils) add_subdirectory(fff_print) diff --git a/tests/fff_print/test_cooling.cpp b/tests/fff_print/test_cooling.cpp index 778a7da401..1f560d53f0 100644 --- a/tests/fff_print/test_cooling.cpp +++ b/tests/fff_print/test_cooling.cpp @@ -14,7 +14,7 @@ using namespace Slic3r; std::unique_ptr make_cooling_buffer( - GCode &gcode, + GCodeGenerator &gcode, const DynamicPrintConfig &config = DynamicPrintConfig{}, const std::vector &extruder_ids = { 0 }) { @@ -65,7 +65,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { const double print_time = 100. / (3000. / 60.); //FIXME slowdown_below_layer_time is rounded down significantly from 1.8s to 1s. config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 0.999) } } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer("G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1", 0, true); bool speed_not_altered = gcode.find("F3000") != gcode.npos; @@ -83,7 +83,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { // Print time of gcode. const double print_time = 50. / (2500. / 60.) + 100. / (3000. / 60.) + 4. / (400. / 60.); config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 1.001) } } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer(gcode_src, 0, true); THEN("speed is altered when elapsed time is lower than slowdown threshold") { @@ -106,7 +106,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time" , int(print_time1 * 0.88) }, { "slowdown_below_layer_time" , int(print_time1 * 0.99) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer(gcode1, 0, true); bool fan_not_activated = gcode.find("M106") == gcode.npos; @@ -119,7 +119,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time", { int(print_time2 + 1.), int(print_time2 + 1.) } }, { "slowdown_below_layer_time", { int(print_time2 + 2.), int(print_time2 + 2.) } } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config, { 0, 1 }); std::string gcode = buffer->process_layer(gcode1 + "T1\nG1 X0 E1 F3000\n", 0, true); THEN("fan is activated for the 1st tool") { @@ -134,7 +134,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { WHEN("G-code block 2") { THEN("slowdown is computed on all objects printing at the same Z") { config.set_deserialize_strict({ { "slowdown_below_layer_time", int(print_time2 * 0.99) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer(gcode2, 0, true); bool ok = gcode.find("F3000") != gcode.npos; @@ -145,7 +145,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time", int(print_time2 * 0.65) }, { "slowdown_below_layer_time", int(print_time2 * 0.7) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); // use an elapsed time which is < the threshold but greater than it when summed twice std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true); @@ -158,7 +158,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time", int(print_time2 + 1) }, { "slowdown_below_layer_time", int(print_time2 + 1) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); // use an elapsed time which is < the threshold but greater than it when summed twice std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true); diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index fa41f88e2f..79064e2c72 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -59,88 +59,110 @@ TriangleMesh mesh(TestMesh m) break; case TestMesh::cube_with_hole: mesh = TriangleMesh( - { {0,0,0}, {0,0,10}, {0,20,0}, {0,20,10}, {20,0,0}, {20,0,10}, {5,5,0}, {15,5,0}, {5,15,0}, {20,20,0}, {15,15,0}, {20,20,10}, {5,5,10}, {5,15,10}, {15,5,10}, {15,15,10} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {0,2,8}, {10,8,9}, {0,8,6}, {0,6,4}, {4,7,9}, {7,10,9}, {2,3,9}, {9,3,11}, {12,1,5}, {13,3,12}, {14,12,5}, {3,1,12}, {11,3,13}, {11,15,5}, {11,13,15}, {15,14,5}, {5,4,9}, {11,5,9}, {8,13,12}, {6,8,12}, {10,15,13}, {8,10,13}, {15,10,14}, {14,10,7}, {14,7,12}, {12,7,6} }); + { {0.f,0.f,0.f}, {0.f,0.f,10.f}, {0.f,20.f,0.f}, {0.f,20.f,10.f}, {20.f,0.f,0.f}, {20.f,0.f,10.f}, {5.f,5.f,0.f}, {15.f,5.f,0.f}, {5.f,15.f,0.f}, {20.f,20.f,0.f}, {15.f,15.f,0.f}, {20.f,20.f,10.f}, {5.f,5.f,10.f}, {5.f,15.f,10.f}, {15.f,5.f,10.f}, {15.f,15.f,10.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {0,2,8}, {10,8,9}, {0,8,6}, {0,6,4}, {4,7,9}, {7,10,9}, {2,3,9}, {9,3,11}, {12,1,5}, {13,3,12}, {14,12,5}, {3,1,12}, {11,3,13}, {11,15,5}, {11,13,15}, {15,14,5}, {5,4,9}, {11,5,9}, {8,13,12}, {6,8,12}, {10,15,13}, {8,10,13}, {15,10,14}, {14,10,7}, {14,7,12}, {12,7,6} }); break; case TestMesh::cube_with_concave_hole: mesh = TriangleMesh( - { {-10,-10,-5}, {-10,-10,5}, {-10,10,-5}, {-10,10,5}, {10,-10,-5}, {10,-10,5}, {-5,-5,-5}, {5,-5,-5}, {5,5,-5}, {5,10,-5}, {-5,5,-5}, Vec3f(3.06161699911402e-16f,5,-5), - {5,0,-5}, {0,0,-5}, {10,5,-5}, {5,10,5}, {-5,-5,5}, {5,0,5}, {5,-5,5}, {-5,5,5}, {10,5,5}, {5,5,5}, Vec3f(3.06161699911402e-16f,5,5), {0,0,5} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {10,2,11}, {11,12,13}, {0,2,10}, {0,10,6}, {0,6,4}, {11,2,8}, {4,7,12}, {4,12,8}, {12,11,8}, {14,4,8}, {2,3,9}, {9,3,15}, {16,1,5}, {17,18,5}, {19,3,16}, {20,21,5}, {18,16,5}, {3,1,16}, {22,3,19}, {21,3,22}, {21,17,5}, {21,22,17}, {21,15,3}, {23,17,22}, {5,4,14}, {20,5,14}, {20,14,21}, {21,14,8}, {9,15,21}, {8,9,21}, {10,19,16}, {6,10,16}, {11,22,19}, {10,11,19}, {13,23,11}, {11,23,22}, {23,13,12}, {17,23,12}, {17,12,18}, {18,12,7}, {18,7,16}, {16,7,6} }); + { {-10.f,-10.f,-5.f}, {-10.f,-10.f,5.f}, {-10.f,10.f,-5.f}, {-10.f,10.f,5.f}, {10.f,-10.f,-5.f}, {10.f,-10.f,5.f}, {-5.f,-5.f,-5.f}, {5.f,-5.f,-5.f}, {5.f,5.f,-5.f}, {5.f,10.f,-5.f}, {-5.f,5.f,-5.f}, {float(3.06161699911402e-16),5.f,-5.f}, + {5.f,0.f,-5.f}, {0.f,0.f,-5.f}, {10.f,5.f,-5.f}, {5.f,10.f,5.f}, {-5.f,-5.f,5.f}, {5.f,0.f,5.f}, {5.f,-5.f,5.f}, {-5.f,5.f,5.f}, {10.f,5.f,5.f}, {5.f,5.f,5.f}, {float(3.06161699911402e-16),5.f,5.f}, {0.f,0.f,5.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {10,2,11}, {11,12,13}, {0,2,10}, {0,10,6}, {0,6,4}, {11,2,8}, {4,7,12}, {4,12,8}, {12,11,8}, {14,4,8}, {2,3,9}, {9,3,15}, {16,1,5}, {17,18,5}, {19,3,16}, {20,21,5}, {18,16,5}, {3,1,16}, {22,3,19}, {21,3,22}, {21,17,5}, {21,22,17}, {21,15,3}, {23,17,22}, {5,4,14}, {20,5,14}, {20,14,21}, {21,14,8}, {9,15,21}, {8,9,21}, {10,19,16}, {6,10,16}, {11,22,19}, {10,11,19}, {13,23,11}, {11,23,22}, {23,13,12}, {17,23,12}, {17,12,18}, {18,12,7}, {18,7,16}, {16,7,6} }); break; case TestMesh::V: mesh = TriangleMesh( - { {-14,0,20}, {-14,15,20}, {0,0,0}, {0,15,0}, {-4,0,20}, {-4,15,20}, {5,0,7.14286f}, {10,0,0}, {24,0,20}, {14,0,20}, {10,15,0}, {5,15,7.14286f}, {14,15,20}, {24,15,20} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {4,0,2}, {6,4,2}, {7,6,2}, {8,9,7}, {9,6,7}, {2,3,7}, {7,3,10}, {1,5,3}, {3,5,11}, {11,12,13}, {11,13,3}, {3,13,10}, {5,4,6}, {11,5,6}, {6,9,11}, {11,9,12}, {12,9,8}, {13,12,8}, {8,7,10}, {13,8,10} }); + { {-14.f,0.f,20.f}, {-14.f,15.f,20.f}, {0.f,0.f,0.f}, {0.f,15.f,0.f}, {-4.f,0.f,20.f}, {-4.f,15.f,20.f}, {5.f,0.f,7.14286f}, {10.f,0.f,0.f}, {24.f,0.f,20.f}, {14.f,0.f,20.f}, {10.f,15.f,0.f}, {5.f,15.f,7.14286f}, {14.f,15.f,20.f}, {24.f,15.f,20.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {4,0,2}, {6,4,2}, {7,6,2}, {8,9,7}, {9,6,7}, {2,3,7}, {7,3,10}, {1,5,3}, {3,5,11}, {11,12,13}, {11,13,3}, {3,13,10}, {5,4,6}, {11,5,6}, {6,9,11}, {11,9,12}, {12,9,8}, {13,12,8}, {8,7,10}, {13,8,10} }); break; case TestMesh::L: mesh = TriangleMesh( - { {0,10,0}, {0,10,10}, {0,20,0}, {0,20,10}, {10,10,0}, {10,10,10}, {20,20,0}, {20,0,0}, {10,0,0}, {20,20,10}, {10,0,10}, {20,0,10} }, - { {0,1,2}, {2,1,3}, {4,5,1}, {0,4,1}, {0,2,4}, {4,2,6}, {4,6,7}, {4,7,8}, {2,3,6}, {6,3,9}, {3,1,5}, {9,3,5}, {10,11,5}, {11,9,5}, {5,4,10}, {10,4,8}, {10,8,7}, {11,10,7}, {11,7,6}, {9,11,6} }); + { {0.f,10.f,0.f}, {0.f,10.f,10.f}, {0.f,20.f,0.f}, {0.f,20.f,10.f}, {10.f,10.f,0.f}, {10.f,10.f,10.f}, {20.f,20.f,0.f}, {20.f,0.f,0.f}, {10.f,0.f,0.f}, {20.f,20.f,10.f}, {10.f,0.f,10.f}, {20.f,0.f,10.f} }, + { {0,1,2}, {2,1,3}, {4,5,1}, {0,4,1}, {0,2,4}, {4,2,6}, {4,6,7}, {4,7,8}, {2,3,6}, {6,3,9}, {3,1,5}, {9,3,5}, {10,11,5}, {11,9,5}, {5,4,10}, {10,4,8}, {10,8,7}, {11,10,7}, {11,7,6}, {9,11,6} }); break; case TestMesh::overhang: - mesh = TriangleMesh( - { {1364.68505859375,614.398010253906,20.002498626709}, {1389.68505859375,614.398010253906,20.002498626709}, {1377.18505859375,589.398986816406,20.002498626709}, {1389.68505859375,589.398986816406,20.002498626709}, {1389.68505859375,564.398986816406,20.0014991760254}, {1364.68505859375,589.398986816406,20.002498626709}, {1364.68505859375,564.398986816406,20.0014991760254}, {1360.93505859375,589.398986816406,17.0014991760254}, {1360.93505859375,585.64697265625,17.0014991760254}, {1357.18505859375,564.398986816406,17.0014991760254}, {1364.68505859375,589.398986816406,17.0014991760254}, {1364.68505859375,571.899963378906,17.0014991760254}, {1364.68505859375,564.398986816406,17.0014991760254}, {1348.43603515625,564.398986816406,17.0014991760254}, {1352.80908203125,589.398986816406,17.0014991760254}, {1357.18408203125,589.398986816406,17.0014991760254}, {1357.18310546875,614.398010253906,17.0014991760254}, {1364.68505859375,606.89599609375,17.0014991760254}, {1364.68505859375,614.398010253906,17.0014991760254}, {1352.18603515625,564.398986816406,20.0014991760254}, {1363.65405273438,589.398986816406,23.3004989624023}, {1359.46704101562,589.398986816406,23.3004989624023}, {1358.37109375,564.398986816406,23.3004989624023}, {1385.56103515625,564.398986816406,23.3004989624023}, {1373.06311035156,589.398986816406,23.3004989624023}, {1368.80810546875,564.398986816406,23.3004989624023}, {1387.623046875,589.398986816406,23.3004989624023}, {1387.623046875,585.276000976562,23.3004989624023}, {1389.68505859375,589.398986816406,23.3004989624023}, {1389.68505859375,572.64599609375,23.3004989624023}, {1389.68505859375,564.398986816406,23.3004989624023}, {1367.77709960938,589.398986816406,23.3004989624023}, {1366.7470703125,564.398986816406,23.3004989624023}, {1354.31201171875,589.398986816406,23.3004989624023}, {1352.18603515625,564.398986816406,23.3004989624023}, {1389.68505859375,614.398010253906,23.3004989624023}, {1377.31701660156,614.398010253906,23.3004989624023}, {1381.43908691406,589.398986816406,23.3004989624023}, {1368.80700683594,614.398010253906,23.3004989624023}, {1368.80810546875,589.398986816406,23.3004989624023}, {1356.43908691406,614.398010253906,23.3004989624023}, {1357.40502929688,589.398986816406,23.3004989624023}, {1360.56201171875,614.398010253906,23.3004989624023}, {1348.705078125,614.398010253906,23.3004989624023}, {1350.44506835938,589.398986816406,23.3004989624023}, {1389.68505859375,606.153015136719,23.3004989624023}, {1347.35205078125,589.398986816406,23.3004989624023}, {1346.56005859375,589.398986816406,23.3004989624023}, {1346.56005859375,594.159912109375,17.0014991760254}, {1346.56005859375,589.398986816406,17.0014991760254}, {1346.56005859375,605.250427246094,23.3004989624023}, {1346.56005859375,614.398010253906,23.3004989624023}, {1346.56005859375,614.398010253906,20.8258285522461}, {1346.56005859375,614.398010253906,17.0014991760254}, {1346.56005859375,564.398986816406,19.10133934021}, {1346.56005859375,567.548583984375,23.3004989624023}, {1346.56005859375,564.398986816406,17.0020332336426}, {1346.56005859375,564.398986816406,23.0018501281738}, {1346.56005859375,564.398986816406,23.3004989624023}, {1346.56005859375,575.118957519531,17.0014991760254}, {1346.56005859375,574.754028320312,23.3004989624023} }, - { {0,1,2}, {2,3,4}, {2,5,0}, {4,6,2}, {2,6,5}, {2,1,3}, {7,8,9}, {10,9,8}, {11,9,10}, {12,9,11}, {9,13,14}, {7,15,16}, {10,17,0}, {10,0,5}, {12,11,6}, {18,16,0}, {6,19,13}, {6,13,9}, {9,12,6}, {17,18,0}, {11,10,5}, {11,5,6}, {14,16,15}, {17,7,18}, {16,18,7}, {14,15,9}, {7,9,15}, {7,17,8}, {10,8,17}, {20,21,22}, {23,24,25}, {26,23,27}, {28,27,23}, {29,28,23}, {30,29,23}, {25,31,32}, {22,33,34}, {35,36,37}, {24,38,39}, {21,40,41}, {38,42,20}, {33,43,44}, {6,4,23}, {6,23,25}, {36,35,1}, {1,0,38}, {1,38,36}, {29,30,4}, {25,32,6}, {40,42,0}, {35,45,1}, {4,3,28}, {4,28,29}, {3,1,45}, {3,45,28}, {22,34,19}, {19,6,32}, {19,32,22}, {42,38,0}, {30,23,4}, {0,16,43}, {0,43,40}, {24,37,36}, {38,24,36}, {24,23,37}, {37,23,26}, {22,32,20}, {20,32,31}, {33,41,40}, {43,33,40}, {45,35,26}, {37,26,35}, {33,44,34}, {44,43,46}, {20,42,21}, {40,21,42}, {31,39,38}, {20,31,38}, {33,22,41}, {21,41,22}, {31,25,39}, {24,39,25}, {26,27,45}, {28,45,27}, {47,48,49}, {47,50,48}, {51,48,50}, {52,48,51}, {53,48,52}, {54,55,56}, {57,55,54}, {58,55,57}, {49,59,47}, {60,56,55}, {59,56,60}, {60,47,59}, {48,53,16}, {56,13,19}, {54,56,19}, {56,59,13}, {59,49,14}, {59,14,13}, {49,48,16}, {49,16,14}, {44,46,60}, {44,60,55}, {51,50,43}, {19,34,58}, {19,58,57}, {53,52,16}, {43,16,52}, {43,52,51}, {57,54,19}, {47,60,46}, {55,58,34}, {55,34,44}, {50,47,46}, {50,46,43} }); + mesh = TriangleMesh( + { {1364.68505859375f,614.398010253906f,20.002498626709f}, {1389.68505859375f,614.398010253906f,20.002498626709f}, {1377.18505859375f,589.398986816406f,20.002498626709f}, {1389.68505859375f,589.398986816406f,20.002498626709f}, {1389.68505859375f,564.398986816406f,20.0014991760254f}, {1364.68505859375f,589.398986816406f,20.002498626709f}, {1364.68505859375f,564.398986816406f,20.0014991760254f}, {1360.93505859375f,589.398986816406f,17.0014991760254f}, {1360.93505859375f,585.64697265625f,17.0014991760254f}, {1357.18505859375f,564.398986816406f,17.0014991760254f}, {1364.68505859375f,589.398986816406f,17.0014991760254f}, {1364.68505859375f,571.899963378906f,17.0014991760254f}, {1364.68505859375f,564.398986816406f,17.0014991760254f}, {1348.43603515625f,564.398986816406f,17.0014991760254f}, {1352.80908203125f,589.398986816406f,17.0014991760254f}, {1357.18408203125f,589.398986816406f,17.0014991760254f}, {1357.18310546875f,614.398010253906f,17.0014991760254f}, {1364.68505859375f,606.89599609375f,17.0014991760254f}, {1364.68505859375f,614.398010253906f,17.0014991760254f}, {1352.18603515625f,564.398986816406f,20.0014991760254f}, {1363.65405273438f,589.398986816406f,23.3004989624023f}, {1359.46704101562f,589.398986816406f,23.3004989624023f}, {1358.37109375f,564.398986816406f,23.3004989624023f}, {1385.56103515625f,564.398986816406f,23.3004989624023f}, {1373.06311035156f,589.398986816406f,23.3004989624023f}, {1368.80810546875f,564.398986816406f,23.3004989624023f}, {1387.623046875f,589.398986816406f,23.3004989624023f}, {1387.623046875f,585.276000976562f,23.3004989624023f}, {1389.68505859375f,589.398986816406f,23.3004989624023f}, {1389.68505859375f,572.64599609375f,23.3004989624023f}, {1389.68505859375f,564.398986816406f,23.3004989624023f}, {1367.77709960938f,589.398986816406f,23.3004989624023f}, {1366.7470703125f,564.398986816406f,23.3004989624023f}, {1354.31201171875f,589.398986816406f,23.3004989624023f}, {1352.18603515625f,564.398986816406f,23.3004989624023f}, {1389.68505859375f,614.398010253906f,23.3004989624023f}, {1377.31701660156f,614.398010253906f,23.3004989624023f}, {1381.43908691406f,589.398986816406f,23.3004989624023f}, {1368.80700683594f,614.398010253906f,23.3004989624023f}, {1368.80810546875f,589.398986816406f,23.3004989624023f}, {1356.43908691406f,614.398010253906f,23.3004989624023f}, {1357.40502929688f,589.398986816406f,23.3004989624023f}, {1360.56201171875f,614.398010253906f,23.3004989624023f}, {1348.705078125f,614.398010253906f,23.3004989624023f}, {1350.44506835938f,589.398986816406f,23.3004989624023f}, {1389.68505859375f,606.153015136719f,23.3004989624023f}, {1347.35205078125f,589.398986816406f,23.3004989624023f}, {1346.56005859375f,589.398986816406f,23.3004989624023f}, {1346.56005859375f,594.159912109375f,17.0014991760254f}, {1346.56005859375f,589.398986816406f,17.0014991760254f}, {1346.56005859375f,605.250427246094f,23.3004989624023f}, {1346.56005859375f,614.398010253906f,23.3004989624023f}, {1346.56005859375f,614.398010253906f,20.8258285522461f}, {1346.56005859375f,614.398010253906f,17.0014991760254f}, {1346.56005859375f,564.398986816406f,19.10133934021f}, {1346.56005859375f,567.548583984375f,23.3004989624023f}, {1346.56005859375f,564.398986816406f,17.0020332336426f}, {1346.56005859375f,564.398986816406f,23.0018501281738f}, {1346.56005859375f,564.398986816406f,23.3004989624023f}, {1346.56005859375f,575.118957519531f,17.0014991760254f}, {1346.56005859375f,574.754028320312f,23.3004989624023f} }, + { {0,1,2}, {2,3,4}, {2,5,0}, {4,6,2}, {2,6,5}, {2,1,3}, {7,8,9}, {10,9,8}, {11,9,10}, {12,9,11}, {9,13,14}, {7,15,16}, {10,17,0}, {10,0,5}, {12,11,6}, {18,16,0}, {6,19,13}, {6,13,9}, {9,12,6}, {17,18,0}, {11,10,5}, {11,5,6}, {14,16,15}, {17,7,18}, {16,18,7}, {14,15,9}, {7,9,15}, {7,17,8}, {10,8,17}, {20,21,22}, {23,24,25}, {26,23,27}, {28,27,23}, {29,28,23}, {30,29,23}, {25,31,32}, {22,33,34}, {35,36,37}, {24,38,39}, {21,40,41}, {38,42,20}, {33,43,44}, {6,4,23}, {6,23,25}, {36,35,1}, {1,0,38}, {1,38,36}, {29,30,4}, {25,32,6}, {40,42,0}, {35,45,1}, {4,3,28}, {4,28,29}, {3,1,45}, {3,45,28}, {22,34,19}, {19,6,32}, {19,32,22}, {42,38,0}, {30,23,4}, {0,16,43}, {0,43,40}, {24,37,36}, {38,24,36}, {24,23,37}, {37,23,26}, {22,32,20}, {20,32,31}, {33,41,40}, {43,33,40}, {45,35,26}, {37,26,35}, {33,44,34}, {44,43,46}, {20,42,21}, {40,21,42}, {31,39,38}, {20,31,38}, {33,22,41}, {21,41,22}, {31,25,39}, {24,39,25}, {26,27,45}, {28,45,27}, {47,48,49}, {47,50,48}, {51,48,50}, {52,48,51}, {53,48,52}, {54,55,56}, {57,55,54}, {58,55,57}, {49,59,47}, {60,56,55}, {59,56,60}, {60,47,59}, {48,53,16}, {56,13,19}, {54,56,19}, {56,59,13}, {59,49,14}, {59,14,13}, {49,48,16}, {49,16,14}, {44,46,60}, {44,60,55}, {51,50,43}, {19,34,58}, {19,58,57}, {53,52,16}, {43,16,52}, {43,52,51}, {57,54,19}, {47,60,46}, {55,58,34}, {55,34,44}, {50,47,46}, {50,46,43} }); break; case TestMesh::_40x10: mesh = TriangleMesh( - { {12.8680295944214,29.5799007415771,12}, {11.7364797592163,29.8480796813965,12}, {11.1571502685547,29.5300102233887,12}, {10.5814504623413,29.9830799102783,12}, {10,29.6000003814697,12}, {9.41855144500732,29.9830799102783,12}, {8.84284687042236,29.5300102233887,12}, {8.26351833343506,29.8480796813965,12}, {7.70256900787354,29.3210391998291,12}, {7.13196802139282,29.5799007415771,12}, {6.59579277038574,28.9761600494385,12}, {6.03920221328735,29.1821594238281,12}, {5.53865718841553,28.5003795623779,12}, {5,28.6602592468262,12}, {4.54657793045044,27.9006500244141,12}, {4.02841377258301,28.0212306976318,12}, {3.63402199745178,27.1856994628906,12}, {3.13758301734924,27.2737407684326,12}, {2.81429696083069,26.3659801483154,12}, {2.33955597877502,26.4278793334961,12}, {2.0993549823761,25.4534206390381,12}, {1.64512205123901,25.4950904846191,12}, {1.49962198734283,24.4613399505615,12}, {1.0636739730835,24.4879894256592,12}, {1.02384400367737,23.4042091369629,12}, {0.603073298931122,23.4202003479004,12}, {0.678958415985107,22.2974300384521,12}, {0.269550800323486,22.3061599731445,12}, {0.469994693994522,21.1571502685547,12}, {0.067615881562233,21.1609306335449,12}, {0.399999290704727,20,12}, {0,20,12}, {0.399999290704727,5,12}, {0,5,12}, {0.456633001565933,4.2804012298584,12}, {0.0615576282143593,4.21782684326172,12}, {0.625140011310577,3.5785219669342,12}, {0.244717106223106,3.45491504669189,12}, {0.901369392871857,2.91164398193359,12}, {0.544967114925385,2.73004698753357,12}, {1.27852201461792,2.29618692398071,12}, {0.954914808273315,2.06107401847839,12}, {1.74730801582336,1.74730801582336,12}, {1.46446597576141,1.46446597576141,12}, {2.29618692398071,1.27852201461792,12}, {2.06107401847839,0.954914808273315,12}, {2.91164398193359,0.901369392871857,12}, {2.73004698753357,0.544967114925385,12}, {3.5785219669342,0.625140011310577,12}, {3.45491504669189,0.244717106223106,12}, {4.2804012298584,0.456633001565933,12}, {4.21782684326172,0.0615576282143593,12}, {5,0.399999290704727,12}, {5,0,12}, {19.6000003814697,0.399999290704727,12}, {20,0,12}, {19.6000003814697,20,12}, {20,20,12}, {19.5300102233887,21.1571502685547,12}, {19.9323806762695,21.1609306335449,12}, {19.3210391998291,22.2974300384521,12}, {19.7304496765137,22.3061599731445,12}, {18.9761600494385,23.4042091369629,12}, {19.3969306945801,23.4202003479004,12}, {18.5003795623779,24.4613399505615,12}, {18.9363307952881,24.4879894256592,12}, {17.9006500244141,25.4534206390381,12}, {18.3548793792725,25.4950904846191,12}, {17.1856994628906,26.3659801483154,12}, {17.6604404449463,26.4278793334961,12}, {16.3659801483154,27.1856994628906,12}, {16.862419128418,27.2737407684326,12}, {15.4534196853638,27.9006500244141,12}, {15.9715900421143,28.0212306976318,12}, {14.4613399505615,28.5003795623779,12}, {15,28.6602592468262,12}, {13.4042100906372,28.9761600494385,12}, {13.9608001708984,29.1821594238281,12}, {12.2974300384521,29.3210391998291,12}, {7.13196802139282,29.5799007415771,0}, {8.26351833343506,29.8480796813965,0}, {8.84284687042236,29.5300102233887,0}, {9.41855144500732,29.9830799102783,0}, {10,29.6000003814697,0}, {10.5814504623413,29.9830799102783,0}, {11.1571502685547,29.5300102233887,0}, {11.7364797592163,29.8480796813965,0}, {12.2974300384521,29.3210391998291,0}, {12.8680295944214,29.5799007415771,0}, {13.4042100906372,28.9761600494385,0}, {13.9608001708984,29.1821594238281,0}, {14.4613399505615,28.5003795623779,0}, {15,28.6602592468262,0}, {15.4534196853638,27.9006500244141,0}, {15.9715900421143,28.0212306976318,0}, {16.3659801483154,27.1856994628906,0}, {16.862419128418,27.2737407684326,0}, {17.1856994628906,26.3659801483154,0}, {17.6604404449463,26.4278793334961,0}, {17.9006500244141,25.4534206390381,0}, {18.3548793792725,25.4950904846191,0}, {18.5003795623779,24.4613399505615,0}, {18.9363307952881,24.4879894256592,0}, {18.9761600494385,23.4042091369629,0}, {19.3969306945801,23.4202003479004,0}, {19.3210391998291,22.2974300384521,0}, {19.7304496765137,22.3061599731445,0}, {19.5300102233887,21.1571502685547,0}, {19.9323806762695,21.1609306335449,0}, {19.6000003814697,20,0}, {20,20,0}, {19.6000003814697,0.399999290704727,0}, {20,0,0}, {5,0.399999290704727,0}, {5,0,0}, {4.2804012298584,0.456633001565933,0}, {4.21782684326172,0.0615576282143593,0}, {3.5785219669342,0.625140011310577,0}, {3.45491504669189,0.244717106223106,0}, {2.91164398193359,0.901369392871857,0}, {2.73004698753357,0.544967114925385,0}, {2.29618692398071,1.27852201461792,0}, {2.06107401847839,0.954914808273315,0}, {1.74730801582336,1.74730801582336,0}, {1.46446597576141,1.46446597576141,0}, {1.27852201461792,2.29618692398071,0}, {0.954914808273315,2.06107401847839,0}, {0.901369392871857,2.91164398193359,0}, {0.544967114925385,2.73004698753357,0}, {0.625140011310577,3.5785219669342,0}, {0.244717106223106,3.45491504669189,0}, {0.456633001565933,4.2804012298584,0}, {0.0615576282143593,4.21782684326172,0}, {0.399999290704727,5,0}, {0,5,0}, {0.399999290704727,20,0}, {0,20,0}, {0.469994693994522,21.1571502685547,0}, {0.067615881562233,21.1609306335449,0}, {0.678958415985107,22.2974300384521,0}, {0.269550800323486,22.3061599731445,0}, {1.02384400367737,23.4042091369629,0}, {0.603073298931122,23.4202003479004,0}, {1.49962198734283,24.4613399505615,0}, {1.0636739730835,24.4879894256592,0}, {2.0993549823761,25.4534206390381,0}, {1.64512205123901,25.4950904846191,0}, {2.81429696083069,26.3659801483154,0}, {2.33955597877502,26.4278793334961,0}, {3.63402199745178,27.1856994628906,0}, {3.13758301734924,27.2737407684326,0}, {4.54657793045044,27.9006500244141,0}, {4.02841377258301,28.0212306976318,0}, {5.53865718841553,28.5003795623779,0}, {5,28.6602592468262,0}, {6.59579277038574,28.9761600494385,0}, {6.03920221328735,29.1821594238281,0}, {7.70256900787354,29.3210391998291,0} }, - { {0,1,2}, {2,1,3}, {2,3,4}, {4,3,5}, {4,5,6}, {6,5,7}, {6,7,8}, {8,7,9}, {8,9,10}, {10,9,11}, {10,11,12}, {12,11,13}, {12,13,14}, {14,13,15}, {14,15,16}, {16,15,17}, {16,17,18}, {18,17,19}, {18,19,20}, {20,19,21}, {20,21,22}, {22,21,23}, {22,23,24}, {24,23,25}, {24,25,26}, {26,25,27}, {26,27,28}, {28,27,29}, {28,29,30}, {30,29,31}, {30,31,32}, {32,31,33}, {32,33,34}, {34,33,35}, {34,35,36}, {36,35,37}, {36,37,38}, {38,37,39}, {38,39,40}, {40,39,41}, {40,41,42}, {42,41,43}, {42,43,44}, {44,43,45}, {44,45,46}, {46,45,47}, {46,47,48}, {48,47,49}, {48,49,50}, {50,49,51}, {50,51,52}, {52,51,53}, {52,53,54}, {54,53,55}, {54,55,56}, {56,55,57}, {56,57,58}, {58,57,59}, {58,59,60}, {60,59,61}, {60,61,62}, {62,61,63}, {62,63,64}, {64,63,65}, {64,65,66}, {66,65,67}, {66,67,68}, {68,67,69}, {68,69,70}, {70,69,71}, {70,71,72}, {72,71,73}, {72,73,74}, {74,73,75}, {74,75,76}, {76,75,77}, {76,77,78}, {78,77,0}, {78,0,2}, {79,80,81}, {81,80,82}, {81,82,83}, {83,82,84}, {83,84,85}, {85,84,86}, {85,86,87}, {87,86,88}, {87,88,89}, {89,88,90}, {89,90,91}, {91,90,92}, {91,92,93}, {93,92,94}, {93,94,95}, {95,94,96}, {95,96,97}, {97,96,98}, {97,98,99}, {99,98,100}, {99,100,101}, {101,100,102}, {101,102,103}, {103,102,104}, {103,104,105}, {105,104,106}, {105,106,107}, {107,106,108}, {107,108,109}, {109,108,110}, {109,110,111}, {111,110,112}, {111,112,113}, {113,112,114}, {113,114,115}, {115,114,116}, {115,116,117}, {117,116,118}, {117,118,119}, {119,118,120}, {119,120,121}, {121,120,122}, {121,122,123}, {123,122,124}, {123,124,125}, {125,124,126}, {125,126,127}, {127,126,128}, {127,128,129}, {129,128,130}, {129,130,131}, {131,130,132}, {131,132,133}, {133,132,134}, {133,134,135}, {135,134,136}, {135,136,137}, {137,136,138}, {137,138,139}, {139,138,140}, {139,140,141}, {141,140,142}, {141,142,143}, {143,142,144}, {143,144,145}, {145,144,146}, {145,146,147}, {147,146,148}, {147,148,149}, {149,148,150}, {149,150,151}, {151,150,152}, {151,152,153}, {153,152,154}, {153,154,155}, {155,154,156}, {155,156,157}, {157,156,79}, {157,79,81}, {57,110,108}, {57,108,59}, {59,108,106}, {59,106,61}, {61,106,104}, {61,104,63}, {63,104,102}, {63,102,65}, {65,102,100}, {65,100,67}, {67,100,98}, {67,98,69}, {69,98,96}, {69,96,71}, {71,96,94}, {71,94,73}, {73,94,92}, {73,92,75}, {75,92,90}, {75,90,77}, {77,90,88}, {77,88,0}, {0,88,86}, {0,86,1}, {1,86,84}, {1,84,3}, {3,84,82}, {3,82,5}, {5,82,80}, {5,80,7}, {7,80,79}, {7,79,9}, {9,79,156}, {9,156,11}, {11,156,154}, {11,154,13}, {13,154,152}, {13,152,15}, {15,152,150}, {15,150,17}, {17,150,148}, {17,148,19}, {19,148,146}, {19,146,21}, {21,146,144}, {21,144,23}, {23,144,142}, {23,142,25}, {25,142,140}, {25,140,27}, {27,140,138}, {27,138,29}, {29,138,136}, {29,136,31}, {33,31,134}, {134,31,136}, {33,134,132}, {33,132,35}, {35,132,130}, {35,130,37}, {37,130,128}, {37,128,39}, {39,128,126}, {39,126,41}, {41,126,124}, {41,124,43}, {43,124,122}, {43,122,45}, {45,122,120}, {45,120,47}, {47,120,118}, {47,118,49}, {49,118,116}, {49,116,51}, {51,116,114}, {51,114,53}, {55,53,112}, {112,53,114}, {57,55,110}, {110,55,112}, {30,135,137}, {30,137,28}, {28,137,139}, {28,139,26}, {26,139,141}, {26,141,24}, {24,141,143}, {24,143,22}, {22,143,145}, {22,145,20}, {20,145,147}, {20,147,18}, {18,147,149}, {18,149,16}, {16,149,151}, {16,151,14}, {14,151,153}, {14,153,12}, {12,153,155}, {12,155,10}, {10,155,157}, {10,157,8}, {8,157,81}, {8,81,6}, {6,81,83}, {6,83,4}, {4,83,85}, {4,85,2}, {2,85,87}, {2,87,78}, {78,87,89}, {78,89,76}, {76,89,91}, {76,91,74}, {74,91,93}, {74,93,72}, {72,93,95}, {72,95,70}, {70,95,97}, {70,97,68}, {68,97,99}, {68,99,66}, {66,99,101}, {66,101,64}, {64,101,103}, {64,103,62}, {62,103,105}, {62,105,60}, {60,105,107}, {60,107,58}, {58,107,109}, {58,109,56}, {30,32,135}, {135,32,133}, {52,113,115}, {52,115,50}, {50,115,117}, {50,117,48}, {48,117,119}, {48,119,46}, {46,119,121}, {46,121,44}, {44,121,123}, {44,123,42}, {42,123,125}, {42,125,40}, {40,125,127}, {40,127,38}, {38,127,129}, {38,129,36}, {36,129,131}, {36,131,34}, {34,131,133}, {34,133,32}, {52,54,113}, {113,54,111}, {54,56,111}, {111,56,109} }); + { {12.8680295944214f,29.5799007415771f,12.f}, {11.7364797592163f,29.8480796813965f,12.f}, {11.1571502685547f,29.5300102233887f,12.f}, {10.5814504623413f,29.9830799102783f,12.f}, {10.f,29.6000003814697f,12.f}, {9.41855144500732f,29.9830799102783f,12.f}, {8.84284687042236f,29.5300102233887f,12.f}, {8.26351833343506f,29.8480796813965f,12.f}, {7.70256900787354f,29.3210391998291f,12.f}, {7.13196802139282f,29.5799007415771f,12.f}, {6.59579277038574f,28.9761600494385f,12.f}, {6.03920221328735f,29.1821594238281f,12.f}, {5.53865718841553f,28.5003795623779f,12.f}, {5.f,28.6602592468262f,12.f}, {4.54657793045044f,27.9006500244141f,12.f}, {4.02841377258301f,28.0212306976318f,12.f}, {3.63402199745178f,27.1856994628906f,12.f}, {3.13758301734924f,27.2737407684326f,12.f}, {2.81429696083069f,26.3659801483154f,12.f}, {2.33955597877502f,26.4278793334961f,12.f}, {2.0993549823761f,25.4534206390381f,12.f}, {1.64512205123901f,25.4950904846191f,12.f}, {1.49962198734283f,24.4613399505615f,12.f}, {1.0636739730835f,24.4879894256592f,12.f}, {1.02384400367737f,23.4042091369629f,12.f}, {0.603073298931122f,23.4202003479004f,12.f}, {0.678958415985107f,22.2974300384521f,12.f}, {0.269550800323486f,22.3061599731445f,12.f}, {0.469994693994522f,21.1571502685547f,12.f}, {0.067615881562233f,21.1609306335449f,12.f}, {0.399999290704727f,20.f,12.f}, {0.f,20.f,12.f}, {0.399999290704727f,5.f,12.f}, {0.f,5.f,12.f}, {0.456633001565933f,4.2804012298584f,12.f}, {0.0615576282143593f,4.21782684326172f,12.f}, {0.625140011310577f,3.5785219669342f,12.f}, {0.244717106223106f,3.45491504669189f,12.f}, {0.901369392871857f,2.91164398193359f,12.f}, {0.544967114925385f,2.73004698753357f,12.f}, {1.27852201461792f,2.29618692398071f,12.f}, {0.954914808273315f,2.06107401847839f,12.f}, {1.74730801582336f,1.74730801582336f,12.f}, {1.46446597576141f,1.46446597576141f,12.f}, {2.29618692398071f,1.27852201461792f,12.f}, {2.06107401847839f,0.954914808273315f,12.f}, {2.91164398193359f,0.901369392871857f,12.f}, {2.73004698753357f,0.544967114925385f,12.f}, {3.5785219669342f,0.625140011310577f,12.f}, {3.45491504669189f,0.244717106223106f,12.f}, {4.2804012298584f,0.456633001565933f,12.f}, {4.21782684326172f,0.0615576282143593f,12.f}, {5.f,0.399999290704727f,12.f}, {5.f,0.f,12.f}, {19.6000003814697f,0.399999290704727f,12.f}, {20.f,0.f,12.f}, {19.6000003814697f,20.f,12.f}, {20.f,20.f,12.f}, {19.5300102233887f,21.1571502685547f,12.f}, {19.9323806762695f,21.1609306335449f,12.f}, {19.3210391998291f,22.2974300384521f,12.f}, {19.7304496765137f,22.3061599731445f,12.f}, {18.9761600494385f,23.4042091369629f,12.f}, {19.3969306945801f,23.4202003479004f,12.f}, {18.5003795623779f,24.4613399505615f,12.f}, {18.9363307952881f,24.4879894256592f,12.f}, {17.9006500244141f,25.4534206390381f,12.f}, {18.3548793792725f,25.4950904846191f,12.f}, {17.1856994628906f,26.3659801483154f,12.f}, {17.6604404449463f,26.4278793334961f,12.f}, {16.3659801483154f,27.1856994628906f,12.f}, {16.862419128418f,27.2737407684326f,12.f}, {15.4534196853638f,27.9006500244141f,12.f}, {15.9715900421143f,28.0212306976318f,12.f}, {14.4613399505615f,28.5003795623779f,12.f}, {15.f,28.6602592468262f,12.f}, {13.4042100906372f,28.9761600494385f,12.f}, {13.9608001708984f,29.1821594238281f,12.f}, {12.2974300384521f,29.3210391998291f,12.f}, {7.13196802139282f,29.5799007415771f,0.f}, {8.26351833343506f,29.8480796813965f,0.f}, {8.84284687042236f,29.5300102233887f,0.f}, {9.41855144500732f,29.9830799102783f,0.f}, {10.f,29.6000003814697f,0.f}, {10.5814504623413f,29.9830799102783f,0.f}, {11.1571502685547f,29.5300102233887f,0.f}, {11.7364797592163f,29.8480796813965f,0.f}, {12.2974300384521f,29.3210391998291f,0.f}, {12.8680295944214f,29.5799007415771f,0.f}, {13.4042100906372f,28.9761600494385f,0.f}, {13.9608001708984f,29.1821594238281f,0.f}, {14.4613399505615f,28.5003795623779f,0.f}, {15.f,28.6602592468262f,0.f}, {15.4534196853638f,27.9006500244141f,0.f}, {15.9715900421143f,28.0212306976318f,0.f}, {16.3659801483154f,27.1856994628906f,0.f}, {16.862419128418f,27.2737407684326f,0.f}, {17.1856994628906f,26.3659801483154f,0.f}, {17.6604404449463f,26.4278793334961f,0.f}, {17.9006500244141f,25.4534206390381f,0.f}, {18.3548793792725f,25.4950904846191f,0.f}, {18.5003795623779f,24.4613399505615f,0.f}, {18.9363307952881f,24.4879894256592f,0.f}, {18.9761600494385f,23.4042091369629f,0.f}, {19.3969306945801f,23.4202003479004f,0.f}, {19.3210391998291f,22.2974300384521f,0.f}, {19.7304496765137f,22.3061599731445f,0.f}, {19.5300102233887f,21.1571502685547f,0.f}, {19.9323806762695f,21.1609306335449f,0.f}, {19.6000003814697f,20.f,0.f}, {20.f,20.f,0.f}, {19.6000003814697f,0.399999290704727f,0.f}, {20.f,0.f,0.f}, {5.f,0.399999290704727f,0.f}, {5.f,0.f,0.f}, {4.2804012298584f,0.456633001565933f,0.f}, {4.21782684326172f,0.0615576282143593f,0.f}, {3.5785219669342f,0.625140011310577f,0.f}, {3.45491504669189f,0.244717106223106f,0.f}, {2.91164398193359f,0.901369392871857f,0.f}, {2.73004698753357f,0.544967114925385f,0.f}, {2.29618692398071f,1.27852201461792f,0.f}, {2.06107401847839f,0.954914808273315f,0.f}, {1.74730801582336f,1.74730801582336f,0.f}, {1.46446597576141f,1.46446597576141f,0.f}, {1.27852201461792f,2.29618692398071f,0.f}, {0.954914808273315f,2.06107401847839f,0.f}, {0.901369392871857f,2.91164398193359f,0.f}, {0.544967114925385f,2.73004698753357f,0.f}, {0.625140011310577f,3.5785219669342f,0.f}, {0.244717106223106f,3.45491504669189f,0.f}, {0.456633001565933f,4.2804012298584f,0.f}, {0.0615576282143593f,4.21782684326172f,0.f}, {0.399999290704727f,5.f,0.f}, {0.f,5.f,0.f}, {0.399999290704727f,20.f,0.f}, {0.f,20.f,0.f}, {0.469994693994522f,21.1571502685547f,0.f}, {0.067615881562233f,21.1609306335449f,0.f}, {0.678958415985107f,22.2974300384521f,0.f}, {0.269550800323486f,22.3061599731445f,0.f}, {1.02384400367737f,23.4042091369629f,0.f}, {0.603073298931122f,23.4202003479004f,0.f}, {1.49962198734283f,24.4613399505615f,0.f}, {1.0636739730835f,24.4879894256592f,0.f}, {2.0993549823761f,25.4534206390381f,0.f}, {1.64512205123901f,25.4950904846191f,0.f}, {2.81429696083069f,26.3659801483154f,0.f}, {2.33955597877502f,26.4278793334961f,0.f}, {3.63402199745178f,27.1856994628906f,0.f}, {3.13758301734924f,27.2737407684326f,0.f}, {4.54657793045044f,27.9006500244141f,0.f}, {4.02841377258301f,28.0212306976318f,0.f}, {5.53865718841553f,28.5003795623779f,0.f}, {5.f,28.6602592468262f,0.f}, {6.59579277038574f,28.9761600494385f,0.f}, {6.03920221328735f,29.1821594238281f,0.f}, {7.70256900787354f,29.3210391998291f,0.f} }, + { {0,1,2}, {2,1,3}, {2,3,4}, {4,3,5}, {4,5,6}, {6,5,7}, {6,7,8}, {8,7,9}, {8,9,10}, {10,9,11}, {10,11,12}, {12,11,13}, {12,13,14}, {14,13,15}, {14,15,16}, {16,15,17}, {16,17,18}, {18,17,19}, {18,19,20}, {20,19,21}, {20,21,22}, {22,21,23}, {22,23,24}, {24,23,25}, {24,25,26}, {26,25,27}, {26,27,28}, {28,27,29}, {28,29,30}, {30,29,31}, {30,31,32}, {32,31,33}, {32,33,34}, {34,33,35}, {34,35,36}, {36,35,37}, {36,37,38}, {38,37,39}, {38,39,40}, {40,39,41}, {40,41,42}, {42,41,43}, {42,43,44}, {44,43,45}, {44,45,46}, {46,45,47}, {46,47,48}, {48,47,49}, {48,49,50}, {50,49,51}, {50,51,52}, {52,51,53}, {52,53,54}, {54,53,55}, {54,55,56}, {56,55,57}, {56,57,58}, {58,57,59}, {58,59,60}, {60,59,61}, {60,61,62}, {62,61,63}, {62,63,64}, {64,63,65}, {64,65,66}, {66,65,67}, {66,67,68}, {68,67,69}, {68,69,70}, {70,69,71}, {70,71,72}, {72,71,73}, {72,73,74}, {74,73,75}, {74,75,76}, {76,75,77}, {76,77,78}, {78,77,0}, {78,0,2}, {79,80,81}, {81,80,82}, {81,82,83}, {83,82,84}, {83,84,85}, {85,84,86}, {85,86,87}, {87,86,88}, {87,88,89}, {89,88,90}, {89,90,91}, {91,90,92}, {91,92,93}, {93,92,94}, {93,94,95}, {95,94,96}, {95,96,97}, {97,96,98}, {97,98,99}, {99,98,100}, {99,100,101}, {101,100,102}, {101,102,103}, {103,102,104}, {103,104,105}, {105,104,106}, {105,106,107}, {107,106,108}, {107,108,109}, {109,108,110}, {109,110,111}, {111,110,112}, {111,112,113}, {113,112,114}, {113,114,115}, {115,114,116}, {115,116,117}, {117,116,118}, {117,118,119}, {119,118,120}, {119,120,121}, {121,120,122}, {121,122,123}, {123,122,124}, {123,124,125}, {125,124,126}, {125,126,127}, {127,126,128}, {127,128,129}, {129,128,130}, {129,130,131}, {131,130,132}, {131,132,133}, {133,132,134}, {133,134,135}, {135,134,136}, {135,136,137}, {137,136,138}, {137,138,139}, {139,138,140}, {139,140,141}, {141,140,142}, {141,142,143}, {143,142,144}, {143,144,145}, {145,144,146}, {145,146,147}, {147,146,148}, {147,148,149}, {149,148,150}, {149,150,151}, {151,150,152}, {151,152,153}, {153,152,154}, {153,154,155}, {155,154,156}, {155,156,157}, {157,156,79}, {157,79,81}, {57,110,108}, {57,108,59}, {59,108,106}, {59,106,61}, {61,106,104}, {61,104,63}, {63,104,102}, {63,102,65}, {65,102,100}, {65,100,67}, {67,100,98}, {67,98,69}, {69,98,96}, {69,96,71}, {71,96,94}, {71,94,73}, {73,94,92}, {73,92,75}, {75,92,90}, {75,90,77}, {77,90,88}, {77,88,0}, {0,88,86}, {0,86,1}, {1,86,84}, {1,84,3}, {3,84,82}, {3,82,5}, {5,82,80}, {5,80,7}, {7,80,79}, {7,79,9}, {9,79,156}, {9,156,11}, {11,156,154}, {11,154,13}, {13,154,152}, {13,152,15}, {15,152,150}, {15,150,17}, {17,150,148}, {17,148,19}, {19,148,146}, {19,146,21}, {21,146,144}, {21,144,23}, {23,144,142}, {23,142,25}, {25,142,140}, {25,140,27}, {27,140,138}, {27,138,29}, {29,138,136}, {29,136,31}, {33,31,134}, {134,31,136}, {33,134,132}, {33,132,35}, {35,132,130}, {35,130,37}, {37,130,128}, {37,128,39}, {39,128,126}, {39,126,41}, {41,126,124}, {41,124,43}, {43,124,122}, {43,122,45}, {45,122,120}, {45,120,47}, {47,120,118}, {47,118,49}, {49,118,116}, {49,116,51}, {51,116,114}, {51,114,53}, {55,53,112}, {112,53,114}, {57,55,110}, {110,55,112}, {30,135,137}, {30,137,28}, {28,137,139}, {28,139,26}, {26,139,141}, {26,141,24}, {24,141,143}, {24,143,22}, {22,143,145}, {22,145,20}, {20,145,147}, {20,147,18}, {18,147,149}, {18,149,16}, {16,149,151}, {16,151,14}, {14,151,153}, {14,153,12}, {12,153,155}, {12,155,10}, {10,155,157}, {10,157,8}, {8,157,81}, {8,81,6}, {6,81,83}, {6,83,4}, {4,83,85}, {4,85,2}, {2,85,87}, {2,87,78}, {78,87,89}, {78,89,76}, {76,89,91}, {76,91,74}, {74,91,93}, {74,93,72}, {72,93,95}, {72,95,70}, {70,95,97}, {70,97,68}, {68,97,99}, {68,99,66}, {66,99,101}, {66,101,64}, {64,101,103}, {64,103,62}, {62,103,105}, {62,105,60}, {60,105,107}, {60,107,58}, {58,107,109}, {58,109,56}, {30,32,135}, {135,32,133}, {52,113,115}, {52,115,50}, {50,115,117}, {50,117,48}, {48,117,119}, {48,119,46}, {46,119,121}, {46,121,44}, {44,121,123}, {44,123,42}, {42,123,125}, {42,125,40}, {40,125,127}, {40,127,38}, {38,127,129}, {38,129,36}, {36,129,131}, {36,131,34}, {34,131,133}, {34,133,32}, {52,54,113}, {113,54,111}, {54,56,111}, {111,56,109} }); break; case TestMesh::sloping_hole: mesh = TriangleMesh( - { {-20,-20,-5}, {-20,-20,5}, {-20,20,-5}, {-20,20,5}, {20,-20,-5}, {20,-20,5}, {4.46294021606445,7.43144989013672,-5}, {20,20,-5}, {-19.1420993804932,0,-5}, {-18.8330993652344,-2.07911992073059,-5}, {-17.9195003509521,-4.06736993789673,-5}, {-16.4412002563477,-5.87785005569458,-5}, {-14.4629001617432,-7.43144989013672,-5}, {-12.0711002349854,-8.66024971008301,-5}, {-9.37016010284424,-9.51056003570557,-5}, {-3.5217399597168,-9.94521999359131,-5}, {-6.4782600402832,-9.94521999359131,-5}, {-0.629840016365051,-9.51056003570557,-5}, {2.07106995582581,-8.66024971008301,-5}, {6.44122982025146,-5.87785005569458,-5}, {4.46294021606445,-7.43144989013672,-5}, {-12.0711002349854,8.66024971008301,-5}, {-9.37016010284424,9.51056003570557,-5}, {7.91947984695435,-4.06736993789673,-5}, {8.83310031890869,-2.07911992073059,-5}, {-6.4782600402832,9.94521999359131,-5}, {-0.629840016365051,9.51056003570557,-5}, {2.07106995582581,8.66024971008301,-5}, {9.14214038848877,0,-5}, {8.83310031890869,2.07911992073059,-5}, {-3.5217399597168,9.94521999359131,-5}, {7.91947984695435,4.06736993789673,-5}, {6.44122982025146,5.87785005569458,-5}, {-14.4629001617432,7.43144989013672,-5}, {-16.4412002563477,5.87785005569458,-5}, {-17.9195003509521,4.06736993789673,-5}, {-18.8330993652344,2.07911992073059,-5}, {20,20,5}, {3.5217399597168,-9.94521999359131,5}, {-8.83310031890869,-2.07911992073059,5}, {-9.14214038848877,0,5}, {-8.83310031890869,2.07911992073059,5}, {6.4782600402832,-9.94521999359131,5}, {-7.91947984695435,4.06736993789673,5}, {-6.44122982025146,5.87785005569458,5}, {-4.46294021606445,7.43144989013672,5}, {-2.07106995582581,8.66024971008301,5}, {0.629840016365051,9.51056003570557,5}, {12.0711002349854,-8.66024971008301,5}, {9.37016010284424,-9.51056003570557,5}, {3.5217399597168,9.94521999359131,5}, {6.4782600402832,9.94521999359131,5}, {9.37016010284424,9.51056003570557,5}, {12.0711002349854,8.66024971008301,5}, {14.4629001617432,7.43144989013672,5}, {16.4412002563477,-5.87785005569458,5}, {14.4629001617432,-7.43144989013672,5}, {16.4412002563477,5.87785005569458,5}, {17.9195003509521,4.06736993789673,5}, {18.8330993652344,-2.07911992073059,5}, {17.9195003509521,-4.06736993789673,5}, {18.8330993652344,2.07911992073059,5}, {19.1420993804932,0,5}, {0.629840016365051,-9.51056003570557,5}, {-2.07106995582581,-8.66024971008301,5}, {-4.46294021606445,-7.43144989013672,5}, {-6.44122982025146,-5.87785005569458,5}, {-7.91947984695435,-4.06736993789673,5} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,2,7}, {0,2,8}, {0,8,9}, {0,9,10}, {0,10,11}, {0,11,12}, {0,12,13}, {0,13,4}, {13,14,4}, {15,4,16}, {17,4,15}, {18,4,17}, {19,4,20}, {18,20,4}, {21,2,22}, {4,19,23}, {4,23,7}, {23,24,7}, {22,2,25}, {26,2,27}, {28,29,7}, {25,2,30}, {29,31,7}, {30,2,26}, {31,32,7}, {27,2,6}, {32,6,7}, {28,7,24}, {33,2,21}, {34,2,33}, {35,2,34}, {36,2,35}, {8,2,36}, {16,4,14}, {2,3,7}, {7,3,37}, {38,1,5}, {3,1,39}, {3,39,40}, {3,40,41}, {42,38,5}, {3,41,43}, {3,43,44}, {37,3,45}, {37,45,46}, {37,46,47}, {48,49,5}, {37,47,50}, {49,42,5}, {37,50,51}, {37,51,52}, {37,52,53}, {37,53,54}, {55,56,5}, {37,54,57}, {37,57,58}, {59,60,5}, {37,58,61}, {37,62,5}, {37,61,62}, {62,59,5}, {60,55,5}, {63,1,38}, {64,1,63}, {65,1,64}, {66,1,65}, {67,1,66}, {39,1,67}, {44,45,3}, {56,48,5}, {5,4,7}, {37,5,7}, {41,40,36}, {36,40,8}, {39,9,40}, {40,9,8}, {43,41,35}, {35,41,36}, {44,43,34}, {34,43,35}, {33,45,44}, {34,33,44}, {21,46,45}, {33,21,45}, {22,47,46}, {21,22,46}, {25,50,47}, {22,25,47}, {30,51,50}, {25,30,50}, {26,52,51}, {30,26,51}, {27,53,52}, {26,27,52}, {6,54,53}, {27,6,53}, {32,57,54}, {6,32,54}, {31,58,57}, {32,31,57}, {29,61,58}, {31,29,58}, {28,62,61}, {29,28,61}, {59,62,28}, {24,59,28}, {60,59,24}, {23,60,24}, {55,60,23}, {19,55,23}, {55,19,56}, {56,19,20}, {56,20,48}, {48,20,18}, {48,18,49}, {49,18,17}, {49,17,42}, {42,17,15}, {42,15,38}, {38,15,16}, {38,16,63}, {63,16,14}, {63,14,64}, {64,14,13}, {64,13,65}, {65,13,12}, {65,12,66}, {66,12,11}, {66,11,67}, {67,11,10}, {67,10,39}, {39,10,9} }); + { {-20.f,-20.f,-5.f}, {-20.f,-20.f,5.f}, {-20.f,20.f,-5.f}, {-20.f,20.f,5.f}, {20.f,-20.f,-5.f}, {20.f,-20.f,5.f}, {4.46294021606445f,7.43144989013672f,-5.f}, {20.f,20.f,-5.f}, {-19.1420993804932f,0.f,-5.f}, {-18.8330993652344f,-2.07911992073059f,-5.f}, {-17.9195003509521f,-4.06736993789673f,-5.f}, {-16.4412002563477f,-5.87785005569458f,-5.f}, {-14.4629001617432f,-7.43144989013672f,-5.f}, {-12.0711002349854f,-8.66024971008301f,-5.f}, {-9.37016010284424f,-9.51056003570557f,-5.f}, {-3.5217399597168f,-9.94521999359131f,-5.f}, {-6.4782600402832f,-9.94521999359131f,-5.f}, {-0.629840016365051f,-9.51056003570557f,-5.f}, {2.07106995582581f,-8.66024971008301f,-5.f}, {6.44122982025146f,-5.87785005569458f,-5.f}, {4.46294021606445f,-7.43144989013672f,-5.f}, {-12.0711002349854f,8.66024971008301f,-5.f}, {-9.37016010284424f,9.51056003570557f,-5.f}, {7.91947984695435f,-4.06736993789673f,-5.f}, {8.83310031890869f,-2.07911992073059f,-5.f}, {-6.4782600402832f,9.94521999359131f,-5.f}, {-0.629840016365051f,9.51056003570557f,-5.f}, {2.07106995582581f,8.66024971008301f,-5.f}, {9.14214038848877f,0.f,-5.f}, {8.83310031890869f,2.07911992073059f,-5.f}, {-3.5217399597168f,9.94521999359131f,-5.f}, {7.91947984695435f,4.06736993789673f,-5.f}, {6.44122982025146f,5.87785005569458f,-5.f}, {-14.4629001617432f,7.43144989013672f,-5.f}, {-16.4412002563477f,5.87785005569458f,-5.f}, {-17.9195003509521f,4.06736993789673f,-5.f}, {-18.8330993652344f,2.07911992073059f,-5.f}, {20.f,20.f,5.f}, {3.5217399597168f,-9.94521999359131f,5.f}, {-8.83310031890869f,-2.07911992073059f,5.f}, {-9.14214038848877f,0.f,5.f}, {-8.83310031890869f,2.07911992073059f,5.f}, {6.4782600402832f,-9.94521999359131f,5.f}, {-7.91947984695435f,4.06736993789673f,5.f}, {-6.44122982025146f,5.87785005569458f,5.f}, {-4.46294021606445f,7.43144989013672f,5.f}, {-2.07106995582581f,8.66024971008301f,5.f}, {0.629840016365051f,9.51056003570557f,5.f}, {12.0711002349854f,-8.66024971008301f,5.f}, {9.37016010284424f,-9.51056003570557f,5.f}, {3.5217399597168f,9.94521999359131f,5.f}, {6.4782600402832f,9.94521999359131f,5.f}, {9.37016010284424f,9.51056003570557f,5.f}, {12.0711002349854f,8.66024971008301f,5.f}, {14.4629001617432f,7.43144989013672f,5.f}, {16.4412002563477f,-5.87785005569458f,5.f}, {14.4629001617432f,-7.43144989013672f,5.f}, {16.4412002563477f,5.87785005569458f,5.f}, {17.9195003509521f,4.06736993789673f,5.f}, {18.8330993652344f,-2.07911992073059f,5.f}, {17.9195003509521f,-4.06736993789673f,5.f}, {18.8330993652344f,2.07911992073059f,5.f}, {19.1420993804932f,0.f,5.f}, {0.629840016365051f,-9.51056003570557f,5.f}, {-2.07106995582581f,-8.66024971008301f,5.f}, {-4.46294021606445f,-7.43144989013672f,5.f}, {-6.44122982025146f,-5.87785005569458f,5.f}, {-7.91947984695435f,-4.06736993789673f,5.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,2,7}, {0,2,8}, {0,8,9}, {0,9,10}, {0,10,11}, {0,11,12}, {0,12,13}, {0,13,4}, {13,14,4}, {15,4,16}, {17,4,15}, {18,4,17}, {19,4,20}, {18,20,4}, {21,2,22}, {4,19,23}, {4,23,7}, {23,24,7}, {22,2,25}, {26,2,27}, {28,29,7}, {25,2,30}, {29,31,7}, {30,2,26}, {31,32,7}, {27,2,6}, {32,6,7}, {28,7,24}, {33,2,21}, {34,2,33}, {35,2,34}, {36,2,35}, {8,2,36}, {16,4,14}, {2,3,7}, {7,3,37}, {38,1,5}, {3,1,39}, {3,39,40}, {3,40,41}, {42,38,5}, {3,41,43}, {3,43,44}, {37,3,45}, {37,45,46}, {37,46,47}, {48,49,5}, {37,47,50}, {49,42,5}, {37,50,51}, {37,51,52}, {37,52,53}, {37,53,54}, {55,56,5}, {37,54,57}, {37,57,58}, {59,60,5}, {37,58,61}, {37,62,5}, {37,61,62}, {62,59,5}, {60,55,5}, {63,1,38}, {64,1,63}, {65,1,64}, {66,1,65}, {67,1,66}, {39,1,67}, {44,45,3}, {56,48,5}, {5,4,7}, {37,5,7}, {41,40,36}, {36,40,8}, {39,9,40}, {40,9,8}, {43,41,35}, {35,41,36}, {44,43,34}, {34,43,35}, {33,45,44}, {34,33,44}, {21,46,45}, {33,21,45}, {22,47,46}, {21,22,46}, {25,50,47}, {22,25,47}, {30,51,50}, {25,30,50}, {26,52,51}, {30,26,51}, {27,53,52}, {26,27,52}, {6,54,53}, {27,6,53}, {32,57,54}, {6,32,54}, {31,58,57}, {32,31,57}, {29,61,58}, {31,29,58}, {28,62,61}, {29,28,61}, {59,62,28}, {24,59,28}, {60,59,24}, {23,60,24}, {55,60,23}, {19,55,23}, {55,19,56}, {56,19,20}, {56,20,48}, {48,20,18}, {48,18,49}, {49,18,17}, {49,17,42}, {42,17,15}, {42,15,38}, {38,15,16}, {38,16,63}, {63,16,14}, {63,14,64}, {64,14,13}, {64,13,65}, {65,13,12}, {65,12,66}, {66,12,11}, {66,11,67}, {67,11,10}, {67,10,39}, {39,10,9} }); break; case TestMesh::ipadstand: mesh = TriangleMesh( - { Vec3f(17.4344673156738,-2.69879599481136e-16,9.5), {14.2814798355103,10,9.5}, {0,0,9.5}, {31.7159481048584,10,9.5}, Vec3f(62.2344741821289,2.06667568800577e-16,20), {31.7159481048584,10,20}, Vec3f(17.4344673156738,-2.69879599481136e-16,20), {62.2344741821289,10,20}, {98.2079696655273,10,0}, Vec3f(98.2079696655273,8.56525380796383e-16,10), {98.2079696655273,0,0}, {98.2079696655273,10,20}, {98.2079696655273,0,20}, Vec3f(81.6609649658203,-4.39753856997999e-16,10), {90.0549850463867,10,10}, {78.5079803466797,10,10}, Vec3f(93.2079696655273,8.56525380796383e-16,10), {14.2814798355103,10,20}, {0,0,20}, Vec3f(87.4344711303711,2.81343962782118e-15,20), {84.2814788818359,10,20}, {0,10,20}, {0,0,0}, {0,10,0}, Vec3f(62.2344741821289,2.06667568800577e-16,30), {66.9609756469727,10,30}, {62.2344741821289,10,30}, Vec3f(70.1139602661133,8.5525763717214e-16,30), {67.7053375244141,10,28.7107200622559}, Vec3f(71.6787109375,1.24046736339707e-15,27.2897701263428) }, - { {0,1,2}, {1,0,3}, {4,5,6}, {5,4,7}, {8,9,10}, {9,11,12}, {11,9,8}, {13,14,15}, {14,13,16}, {17,2,1}, {2,17,18}, {19,11,20}, {11,19,12}, {17,21,18}, {21,2,18}, {2,21,22}, {22,21,23}, {8,22,23}, {22,8,10}, {24,25,26}, {25,24,27}, {23,1,8}, {1,23,21}, {1,21,17}, {5,15,3}, {15,5,7}, {15,7,28}, {28,7,26}, {28,26,25}, {8,14,11}, {14,8,3}, {3,8,1}, {14,3,15}, {11,14,20}, {26,4,24}, {4,26,7}, {12,16,9}, {16,12,19}, {29,4,13}, {4,29,24}, {24,29,27}, {9,22,10}, {22,9,0}, {0,9,16}, {0,16,13}, {0,13,6}, {6,13,4}, {2,22,0}, {19,14,16}, {14,19,20}, {15,29,13}, {29,25,27}, {25,29,15}, {25,15,28}, {6,3,0}, {3,6,5} }); + { {17.4344673156738f,float(-2.69879599481136e-16),9.5f}, {14.2814798355103f,10.f,9.5f}, {0.f,0.f,9.5f}, {31.7159481048584f,10.f,9.5f}, {62.2344741821289f,float(2.06667568800577e-16),20.f}, {31.7159481048584f,10.f,20.f}, {17.4344673156738f,float(-2.69879599481136e-16),20.f}, {62.2344741821289f,10.f,20.f}, {98.2079696655273f,10.f,0.f}, {98.2079696655273f,float(8.56525380796383e-16),10.f}, {98.2079696655273f,0.f,0.f}, {98.2079696655273f,10.f,20.f}, {98.2079696655273f,0.f,20.f}, {81.6609649658203f,float(-4.39753856997999e-16),10.f}, {90.0549850463867f,10.f,10.f}, {78.5079803466797f,10.f,10.f}, {93.2079696655273f,float(8.56525380796383e-16),10.f}, {14.2814798355103f,10.f,20.f}, {0.f,0.f,20.f}, {87.4344711303711f,float(2.81343962782118e-15),20.f}, {84.2814788818359f,10.f,20.f}, {0.f,10.f,20.f}, {0.f,0.f,0.f}, {0.f,10.f,0.f}, {62.2344741821289f,float(2.06667568800577e-16),30.f}, {66.9609756469727f,10.f,30.f}, {62.2344741821289f,10.f,30.f}, {70.1139602661133f,float(8.5525763717214e-16),30.f}, {67.7053375244141f,10.f,28.7107200622559f}, {71.6787109375f,float(1.24046736339707e-15),27.2897701263428f} }, + { {0,1,2}, {1,0,3}, {4,5,6}, {5,4,7}, {8,9,10}, {9,11,12}, {11,9,8}, {13,14,15}, {14,13,16}, {17,2,1}, {2,17,18}, {19,11,20}, {11,19,12}, {17,21,18}, {21,2,18}, {2,21,22}, {22,21,23}, {8,22,23}, {22,8,10}, {24,25,26}, {25,24,27}, {23,1,8}, {1,23,21}, {1,21,17}, {5,15,3}, {15,5,7}, {15,7,28}, {28,7,26}, {28,26,25}, {8,14,11}, {14,8,3}, {3,8,1}, {14,3,15}, {11,14,20}, {26,4,24}, {4,26,7}, {12,16,9}, {16,12,19}, {29,4,13}, {4,29,24}, {24,29,27}, {9,22,10}, {22,9,0}, {0,9,16}, {0,16,13}, {0,13,6}, {6,13,4}, {2,22,0}, {19,14,16}, {14,19,20}, {15,29,13}, {29,25,27}, {25,29,15}, {25,15,28}, {6,3,0}, {3,6,5} }); break; case TestMesh::A: mesh = TriangleMesh( - { {513.075988769531,51.6074333190918,36.0009002685547}, {516.648803710938,51.7324333190918,36.0009002685547}, {513.495178222656,51.7324333190918,36.0009002685547}, {489.391204833984,51.4824333190918,24.0011005401611}, {488.928588867188,51.7324333190918,24.0011005401611}, {492.06201171875,51.7324333190918,24.0011005401611}, {496.840393066406,51.2324333190918,24.0011005401611}, {495.195404052734,51.7324333190918,24.0011005401611}, {498.981994628906,51.7324333190918,24.0011005401611}, {506.966613769531,51.6074333190918,24.0011005401611}, {510.342010498047,51.7324333190918,24.0011005401611}, {507.163818359375,51.6074333190918,24.0011005401611}, {512.515380859375,54.7190322875977,36.0009002685547}, {514.161987304688,54.5058326721191,36.0009002685547}, {493.06201171875,54.7190322875977,36.0009002685547}, {495.195404052734,51.7324333190918,36.0009002685547}, {496.195404052734,54.7190322875977,36.0009002685547}, {497.195404052734,57.7058334350586,36.0009002685547}, {500.851989746094,60.2658309936523,36.0009002685547}, {498.915405273438,62.8258323669434,36.0009002685547}, {506.701995849609,62.8258323669434,36.0009002685547}, {503.648590087891,60.2658309936523,36.0009002685547}, {508.381805419922,57.7058334350586,36.0009002685547}, {496.418792724609,60.052433013916,36.0009002685547}, {506.515197753906,72.2124328613281,36.0009002685547}, {502.808807373047,74.5324325561523,36.0009002685547}, {503.781982421875,71.6058349609375,36.0009002685547}, {515.358764648438,55.4658317565918,36.0009002685547}, {499.375183105469,76.9058380126953,36.0009002685547}, {501.168792724609,78.0658340454102,36.0009002685547}, {504.568786621094,78.0658340454102,36.0009002685547}, {506.32861328125,81.599235534668,36.0009002685547}, {502.928588867188,81.599235534668,36.0009002685547}, {499.528594970703,81.599235534668,36.0009002685547}, {498.20361328125,77.8658294677734,36.0009002685547}, {495.195404052734,51.7324333190918,30.0011005401611}, {498.981994628906,51.7324333190918,27.0011005401611}, {506.555206298828,51.7324333190918,33.0009002685547}, {506.555206298828,51.7324333190918,36.0009002685547}, {510.342010498047,51.7324333190918,36.0009002685547}, {512.515380859375,54.7190322875977,24.0011005401611}, {509.361999511719,54.7190322875977,24.0011005401611}, {508.381805419922,57.7058334350586,24.0011005401611}, {506.701995849609,62.8258323669434,24.0011005401611}, {509.188812255859,60.052433013916,24.0011005401611}, {493.06201171875,54.7190322875977,24.0011005401611}, {503.648590087891,60.2658309936523,24.0011005401611}, {500.851989746094,60.2658309936523,24.0011005401611}, {498.915405273438,62.8258323669434,24.0011005401611}, {502.808807373047,62.8258323669434,24.0011005401611}, {491.425201416016,54.5058326721191,24.0011005401611}, {506.421813964844,76.9058380126953,24.0011005401611}, {502.808807373047,74.5324325561523,24.0011005401611}, {504.568786621094,78.0658340454102,24.0011005401611}, {506.32861328125,81.599235534668,24.0011005401611}, {507.618804931641,77.8658294677734,24.0011005401611}, {499.221801757812,72.2124328613281,24.0011005401611}, {501.835388183594,71.6058349609375,24.0011005401611}, {501.168792724609,78.0658340454102,24.0011005401611}, {499.528594970703,81.599235534668,24.0011005401611}, {502.048583984375,79.8324356079102,24.0011005401611}, {490.253601074219,55.4658317565918,24.0011005401611}, {488.928588867188,51.7324333190918,30.0011005401611}, {488.928588867188,51.7324333190918,36.0009002685547}, {490.253601074219,55.4658317565918,31.5009002685547}, {498.20361328125,77.8658294677734,34.5009002685547}, {508.381805419922,57.7058334350586,30.0011005401611}, {505.585388183594,57.7058334350586,27.0011005401611}, {502.788818359375,57.7058334350586,36.0009002685547}, {499.992004394531,57.7058334350586,33.0009002685547}, {509.851989746094,53.2258338928223,33.0009002685547}, {509.361999511719,54.7190322875977,36.0009002685547}, {508.871795654297,56.2124328613281,27.0011005401611}, {496.695404052734,56.2124328613281,33.0009002685547}, {495.695404052734,53.2258338928223,27.0011005401611}, {506.32861328125,81.599235534668,30.0011005401611}, {507.618804931641,77.8658294677734,25.5011005401611}, {515.358764648438,55.4658317565918,34.5009002685547}, {501.228607177734,81.599235534668,33.0009002685547}, {504.628601074219,81.599235534668,27.0011005401611}, {503.781982421875,71.6058349609375,33.0009002685547}, {502.808807373047,74.5324325561523,30.0011005401611}, {498.915405273438,62.8258323669434,30.0011005401611}, {500.861999511719,62.8258323669434,27.0011005401611}, {502.808807373047,62.8258323669434,36.0009002685547}, {504.755187988281,62.8258323669434,33.0009002685547}, {501.835388183594,71.6058349609375,33.0009002685547}, {499.888793945312,65.7524337768555,33.0009002685547}, {499.888793945312,65.7524337768555,36.0009002685547}, {513.128601074219,51.4824333190918,36.0009002685547}, {513.075988769531,51.6074333190918,24.0011005401611}, {516.648803710938,51.7324333190918,24.0011005401611}, {513.128601074219,51.4824333190918,24.0011005401611}, {513.495178222656,51.7324333190918,24.0011005401611}, {506.966613769531,51.6074333190918,36.0009002685547}, {507.163818359375,51.6074333190918,36.0009002685547}, {490.337799072266,51.4824333190918,24.0011005401611}, {489.391204833984,51.4824333190918,36.0009002685547}, {492.06201171875,51.7324333190918,36.0009002685547}, {490.337799072266,51.4824333190918,36.0009002685547}, {513.233764648438,51.2324333190918,24.0011005401611}, {513.233764648438,51.2324333190918,36.0009002685547}, {504.773803710938,51.4824333190918,36.0009002685547}, {504.773803710938,51.4824333190918,24.0011005401611}, {489.266998291016,51.2324333190918,24.0011005401611}, {489.266998291016,51.2324333190918,36.0009002685547}, {490.253601074219,55.4658317565918,25.5011005401611}, {499.528594970703,81.599235534668,30.0011005401611}, {498.20361328125,77.8658294677734,31.5009002685547}, {515.358764648438,55.4658317565918,28.5011005401611}, {515.358764648438,55.4658317565918,25.5011005401611}, {495.246795654297,61.0124320983887,36.0009002685547}, {490.253601074219,55.4658317565918,34.5009002685547}, {490.253601074219,55.4658317565918,36.0009002685547}, {494.228607177734,66.6658325195312,24.0011005401611}, {499.068786621094,67.5192337036133,24.0011005401611}, {498.20361328125,77.8658294677734,25.5011005401611}, {498.20361328125,77.8658294677734,24.0011005401611}, {506.608795166016,67.5192337036133,36.0009002685547}, {509.09521484375,64.7458343505859,36.0009002685547}, {507.618804931641,77.8658294677734,34.5009002685547}, {507.618804931641,77.8658294677734,36.0009002685547}, {510.385406494141,61.0124320983887,24.0011005401611}, {515.358764648438,55.4658317565918,24.0011005401611}, {489.32861328125,47.7324333190918,31.5009002685547}, {492.95361328125,47.7324333190918,33.5634994506836}, {489.32861328125,47.7324333190918,34.5009002685547}, {489.32861328125,47.7324333190918,28.5011005401611}, {489.32861328125,47.7324333190918,25.5011005401611}, {492.95361328125,47.7324333190918,26.4385013580322}, {492.95361328125,47.7324333190918,30.5635013580322}, {492.95361328125,47.7324333190918,32.0634994506836}, {492.95361328125,47.7324333190918,31.3135013580322}, {492.95361328125,47.7324333190918,35.4384994506836}, {489.32861328125,47.7324333190918,36.0009002685547}, {492.95361328125,47.7324333190918,34.3134994506836}, {492.95361328125,47.7324333190918,34.6884994506836}, {492.95361328125,47.7324333190918,27.9385013580322}, {492.95361328125,47.7324333190918,28.6885013580322}, {492.95361328125,47.7324333190918,29.0635013580322}, {489.32861328125,47.7324333190918,24.0011005401611}, {492.95361328125,47.7324333190918,24.5635013580322}, {492.95361328125,47.7324333190918,25.6885013580322}, {492.95361328125,47.7324333190918,25.3135013580322}, {492.95361328125,47.7324333190918,24.1885013580322}, {492.95361328125,47.7324333190918,24.0011005401611}, {513.443786621094,50.7324333190918,24.0011005401611}, {492.95361328125,47.7324333190918,35.8134994506836}, {492.95361328125,47.7324333190918,36.0009002685547}, {513.443786621094,50.7324333190918,36.0009002685547}, {506.350402832031,51.4824333190918,36.0009002685547}, {506.350402832031,51.4824333190918,24.0011005401611}, {492.743804931641,48.2324333190918,24.0011005401611}, {492.638793945312,48.4824333190918,24.0011005401611}, {492.743804931641,48.2324333190918,36.0009002685547}, {492.638793945312,48.4824333190918,36.0009002685547}, {490.089599609375,50.9824333190918,36.0009002685547}, {490.089599609375,50.9824333190918,24.0011005401611}, {510.342010498047,51.7324333190918,30.0011005401611}, {499.068786621094,67.5192337036133,36.0009002685547}, {494.228607177734,66.6658325195312,36.0009002685547}, {499.375183105469,76.9058380126953,24.0011005401611}, {506.421813964844,76.9058380126953,36.0009002685547}, {506.608795166016,67.5192337036133,24.0011005401611}, {505.728607177734,65.7524337768555,24.0011005401611}, {509.09521484375,64.7458343505859,24.0011005401611}, {506.701995849609,62.8258323669434,30.0011005401611}, {505.728607177734,65.7524337768555,27.0011005401611}, {501.835388183594,71.6058349609375,27.0011005401611}, {499.888793945312,65.7524337768555,27.0011005401611}, {494.228607177734,66.6658325195312,30.0011005401611}, {495.553588867188,70.3992309570312,28.5011005401611}, {492.903594970703,62.9324340820312,28.5011005401611}, {495.553588867188,70.3992309570312,31.5009002685547}, {492.903594970703,62.9324340820312,31.5009002685547}, {511.488800048828,66.6658325195312,24.0011005401611}, {511.488800048828,66.6658325195312,30.0011005401611}, {512.778564453125,62.9324340820312,28.5011005401611}, {515.358764648438,55.4658317565918,31.5009002685547}, {507.618804931641,77.8658294677734,31.5009002685547}, {510.198791503906,70.3992309570312,28.5011005401611}, {511.488800048828,66.6658325195312,36.0009002685547}, {512.778564453125,62.9324340820312,31.5009002685547}, {510.198791503906,70.3992309570312,31.5009002685547}, {502.788818359375,57.7058334350586,24.0011005401611}, {497.195404052734,57.7058334350586,30.0011005401611}, {492.903594970703,62.9324340820312,34.5009002685547}, {492.903594970703,62.9324340820312,36.0009002685547}, {495.553588867188,70.3992309570312,24.0011005401611}, {496.725189208984,69.4392318725586,24.0011005401611}, {495.553588867188,70.3992309570312,25.5011005401611}, {495.246795654297,61.0124320983887,24.0011005401611}, {492.903594970703,62.9324340820312,25.5011005401611}, {492.903594970703,62.9324340820312,24.0011005401611}, {495.553588867188,70.3992309570312,36.0009002685547}, {496.725189208984,69.4392318725586,36.0009002685547}, {495.553588867188,70.3992309570312,34.5009002685547}, {510.198791503906,70.3992309570312,36.0009002685547}, {509.002014160156,69.4392318725586,36.0009002685547}, {510.198791503906,70.3992309570312,34.5009002685547}, {512.778564453125,62.9324340820312,25.5011005401611}, {512.778564453125,62.9324340820312,24.0011005401611}, {510.198791503906,70.3992309570312,24.0011005401611}, {509.002014160156,69.4392318725586,24.0011005401611}, {510.198791503906,70.3992309570312,25.5011005401611}, {510.385406494141,61.0124320983887,36.0009002685547}, {512.778564453125,62.9324340820312,34.5009002685547}, {512.778564453125,62.9324340820312,36.0009002685547}, {496.840393066406,51.2324333190918,36.0009002685547}, {498.981994628906,51.7324333190918,36.0009002685547}, {498.981994628906,51.7324333190918,33.0009002685547}, {506.555206298828,51.7324333190918,24.0011005401611}, {506.555206298828,51.7324333190918,27.0011005401611}, {503.82861328125,47.7324333190918,30.7509002685547}, {507.45361328125,47.7324333190918,32.8134994506836}, {503.82861328125,47.7324333190918,33.7509002685547}, {503.82861328125,47.7324333190918,29.2511005401611}, {503.82861328125,47.7324333190918,26.2511005401611}, {507.45361328125,47.7324333190918,27.1885013580322}, {493.921813964844,57.2792320251465,36.0009002685547}, {491.425201416016,54.5058326721191,36.0009002685547}, {497.195404052734,57.7058334350586,24.0011005401611}, {496.418792724609,60.052433013916,24.0011005401611}, {509.188812255859,60.052433013916,36.0009002685547}, {511.675415039062,57.2792320251465,24.0011005401611}, {514.161987304688,54.5058326721191,24.0011005401611}, {507.45361328125,47.7324333190918,34.3134994506836}, {503.82861328125,47.7324333190918,35.2509002685547}, {507.45361328125,47.7324333190918,25.6885013580322}, {503.82861328125,47.7324333190918,24.7511005401611}, {500.20361328125,47.7324333190918,31.6885013580322}, {500.20361328125,47.7324333190918,28.3135013580322}, {500.20361328125,47.7324333190918,30.1885013580322}, {507.45361328125,47.7324333190918,29.8135013580322}, {507.45361328125,47.7324333190918,31.3135013580322}, {507.45361328125,47.7324333190918,30.5635013580322}, {503.82861328125,47.7324333190918,36.0009002685547}, {507.45361328125,47.7324333190918,35.4384994506836}, {507.45361328125,47.7324333190918,35.0634994506836}, {507.45361328125,47.7324333190918,28.6885013580322}, {507.45361328125,47.7324333190918,29.4385013580322}, {503.82861328125,47.7324333190918,24.0011005401611}, {507.45361328125,47.7324333190918,24.5635013580322}, {507.45361328125,47.7324333190918,24.9385013580322}, {500.20361328125,47.7324333190918,34.6884994506836}, {500.20361328125,47.7324333190918,33.1884994506836}, {500.20361328125,47.7324333190918,33.9384994506836}, {500.20361328125,47.7324333190918,25.3135013580322}, {500.20361328125,47.7324333190918,26.8135013580322}, {500.20361328125,47.7324333190918,26.0635013580322}, {500.20361328125,47.7324333190918,30.9385013580322}, {500.20361328125,47.7324333190918,35.0634994506836}, {500.20361328125,47.7324333190918,35.4384994506836}, {500.20361328125,47.7324333190918,29.0635013580322}, {500.20361328125,47.7324333190918,29.4385013580322}, {500.20361328125,47.7324333190918,24.9385013580322}, {500.20361328125,47.7324333190918,24.5635013580322}, {507.45361328125,47.7324333190918,24.1885013580322}, {507.45361328125,47.7324333190918,24.0011005401611}, {513.86376953125,49.7324333190918,24.0011005401611}, {507.45361328125,47.7324333190918,35.8134994506836}, {507.45361328125,47.7324333190918,36.0009002685547}, {513.86376953125,49.7324333190918,36.0009002685547}, {500.20361328125,47.7324333190918,24.1885013580322}, {500.20361328125,47.7324333190918,24.0011005401611}, {502.988800048828,49.7324333190918,24.0011005401611}, {500.20361328125,47.7324333190918,35.8134994506836}, {500.20361328125,47.7324333190918,36.0009002685547}, {502.988800048828,49.7324333190918,36.0009002685547}, {504.755187988281,62.8258323669434,27.0011005401611}, {499.205383300781,51.2324333190918,36.0009002685547}, {498.786193847656,51.1074333190918,36.0009002685547}, {502.358795166016,51.2324333190918,36.0009002685547}, {499.205383300781,51.2324333190918,24.0011005401611}, {502.358795166016,51.2324333190918,24.0011005401611}, {498.786193847656,51.1074333190918,24.0011005401611}, {502.568786621094,50.7324333190918,24.0011005401611}, {505.931213378906,51.3574333190918,24.0011005401611}, {509.503601074219,51.4824333190918,24.0011005401611}, {502.568786621094,50.7324333190918,36.0009002685547}, {505.931213378906,51.3574333190918,36.0009002685547}, {509.503601074219,51.4824333190918,36.0009002685547}, {499.048583984375,50.4824333190918,36.0009002685547}, {492.428588867188,48.9824333190918,36.0009002685547}, {499.048583984375,50.4824333190918,24.0011005401611}, {492.428588867188,48.9824333190918,24.0011005401611}, {506.088806152344,50.9824333190918,24.0011005401611}, {506.036010742188,51.1074333190918,24.0011005401611}, {506.088806152344,50.9824333190918,36.0009002685547}, {506.036010742188,51.1074333190918,36.0009002685547}, {498.891204833984,50.8574333190918,36.0009002685547}, {498.943786621094,50.7324333190918,36.0009002685547}, {498.891204833984,50.8574333190918,24.0011005401611}, {498.943786621094,50.7324333190918,24.0011005401611}, {499.573608398438,49.2324333190918,24.0011005401611}, {499.783813476562,48.7324333190918,24.0011005401611}, {499.573608398438,49.2324333190918,36.0009002685547}, {499.783813476562,48.7324333190918,36.0009002685547}, {506.403594970703,50.2324333190918,24.0011005401611}, {506.298797607422,50.4824333190918,24.0011005401611}, {506.403594970703,50.2324333190918,36.0009002685547}, {506.298797607422,50.4824333190918,36.0009002685547}, {501.228607177734,81.599235534668,27.0011005401611}, {502.928588867188,81.599235534668,24.0011005401611}, {499.2587890625,49.9824333190918,36.0009002685547}, {499.363800048828,49.7324333190918,36.0009002685547}, {499.2587890625,49.9824333190918,24.0011005401611}, {499.363800048828,49.7324333190918,24.0011005401611}, {496.695404052734,56.2124328613281,27.0011005401611}, {496.195404052734,54.7190322875977,24.0011005401611}, {509.851989746094,53.2258338928223,27.0011005401611}, {493.464782714844,51.1074333190918,36.0009002685547}, {493.464782714844,51.1074333190918,24.0011005401611}, {502.768798828125,51.7324333190918,24.0011005401611}, {500.215789794922,51.3574333190918,24.0011005401611}, {497.628601074219,51.2324333190918,24.0011005401611}, {502.768798828125,51.7324333190918,36.0009002685547}, {500.215789794922,51.3574333190918,36.0009002685547}, {497.628601074219,51.2324333190918,36.0009002685547}, {507.033813476562,48.7324333190918,24.0011005401611}, {506.823791503906,49.2324333190918,24.0011005401611}, {507.033813476562,48.7324333190918,36.0009002685547}, {506.823791503906,49.2324333190918,36.0009002685547}, {494.4501953125,51.1074333190918,24.0011005401611}, {494.4501953125,51.1074333190918,36.0009002685547}, {500.807006835938,51.3574333190918,36.0009002685547}, {503.591186523438,51.4824333190918,36.0009002685547}, {503.591186523438,51.4824333190918,24.0011005401611}, {500.807006835938,51.3574333190918,24.0011005401611}, {505.728607177734,65.7524337768555,36.0009002685547}, {505.728607177734,65.7524337768555,33.0009002685547}, {499.221801757812,72.2124328613281,36.0009002685547}, {501.835388183594,71.6058349609375,36.0009002685547}, {506.515197753906,72.2124328613281,24.0011005401611}, {503.781982421875,71.6058349609375,24.0011005401611}, {503.781982421875,71.6058349609375,27.0011005401611}, {499.888793945312,65.7524337768555,24.0011005401611}, {495.695404052734,53.2258338928223,33.0009002685547}, {516.648803710938,51.7324333190918,30.0011005401611}, {498.20361328125,77.8658294677734,28.5011005401611}, {505.585388183594,57.7058334350586,33.0009002685547}, {508.871795654297,56.2124328613281,33.0009002685547}, {499.992004394531,57.7058334350586,27.0011005401611}, {504.628601074219,81.599235534668,33.0009002685547}, {500.861999511719,62.8258323669434,33.0009002685547}, {496.878601074219,74.1324310302734,27.0011005401611}, {496.878601074219,74.1324310302734,33.0009002685547}, {491.57861328125,59.199031829834,27.0011005401611}, {490.253601074219,55.4658317565918,28.5011005401611}, {491.57861328125,59.199031829834,33.0009002685547}, {514.068786621094,59.199031829834,27.0011005401611}, {514.068786621094,59.199031829834,33.0009002685547}, {508.908813476562,74.1324310302734,27.0011005401611}, {507.618804931641,77.8658294677734,28.5011005401611}, {508.908813476562,74.1324310302734,33.0009002685547}, {491.271789550781,50.9824333190918,36.0009002685547}, {490.877807617188,50.9824333190918,36.0009002685547}, {491.271789550781,50.9824333190918,24.0011005401611}, {490.877807617188,50.9824333190918,24.0011005401611}, {495.213806152344,50.9824333190918,36.0009002685547}, {493.636993408203,50.9824333190918,36.0009002685547}, {495.213806152344,50.9824333190918,24.0011005401611}, {493.636993408203,50.9824333190918,24.0011005401611}, {503.985412597656,51.4824333190918,36.0009002685547}, {503.985412597656,51.4824333190918,24.0011005401611}, {511.675415039062,57.2792320251465,36.0009002685547}, {493.921813964844,57.2792320251465,24.0011005401611}, {502.768798828125,51.7324333190918,30.0011005401611}, {506.555206298828,51.7324333190918,30.0011005401611}, {498.981994628906,51.7324333190918,30.0011005401611}, {492.848815917969,50.9824333190918,24.0011005401611}, {492.848815917969,50.9824333190918,36.0009002685547}, {500.861999511719,68.6792297363281,36.0009002685547}, {500.861999511719,68.6792297363281,24.0011005401611}, {496.878601074219,74.1324310302734,24.0011005401611}, {496.878601074219,74.1324310302734,36.0009002685547}, {504.755187988281,68.6792297363281,24.0011005401611}, {504.755187988281,68.6792297363281,36.0009002685547}, {508.908813476562,74.1324310302734,36.0009002685547}, {508.908813476562,74.1324310302734,24.0011005401611}, {505.728607177734,65.7524337768555,30.0011005401611}, {504.755187988281,68.6792297363281,30.0011005401611}, {503.781982421875,71.6058349609375,30.0011005401611}, {500.861999511719,68.6792297363281,30.0011005401611}, {499.888793945312,65.7524337768555,30.0011005401611}, {501.835388183594,71.6058349609375,30.0011005401611}, {491.57861328125,59.199031829834,24.0011005401611}, {491.57861328125,59.199031829834,36.0009002685547}, {514.068786621094,59.199031829834,36.0009002685547}, {514.068786621094,59.199031829834,24.0011005401611}, {511.07861328125,47.7324333190918,34.8759002685547}, {511.07861328125,47.7324333190918,31.8759002685547}, {514.70361328125,47.7324333190918,33.9384994506836}, {511.07861328125,47.7324333190918,25.1261005401611}, {514.70361328125,47.7324333190918,26.0635013580322}, {511.07861328125,47.7324333190918,28.1261005401611}, {502.788818359375,57.7058334350586,30.0011005401611}, {502.048583984375,79.8324356079102,36.0009002685547}, {514.70361328125,47.7324333190918,30.9385013580322}, {511.07861328125,47.7324333190918,30.3759002685547}, {514.70361328125,47.7324333190918,29.0635013580322}, {511.07861328125,47.7324333190918,29.6261005401611}, {496.57861328125,47.7324333190918,31.1259002685547}, {496.57861328125,47.7324333190918,32.6259002685547}, {496.57861328125,47.7324333190918,34.1259002685547}, {496.57861328125,47.7324333190918,28.8761005401611}, {496.57861328125,47.7324333190918,27.3761005401611}, {496.57861328125,47.7324333190918,25.8761005401611}, {496.57861328125,47.7324333190918,29.6261005401611}, {514.70361328125,47.7324333190918,35.4384994506836}, {511.07861328125,47.7324333190918,35.6259002685547}, {514.70361328125,47.7324333190918,24.5635013580322}, {511.07861328125,47.7324333190918,24.3761005401611}, {496.57861328125,47.7324333190918,34.8759002685547}, {496.57861328125,47.7324333190918,25.1261005401611}, {496.57861328125,47.7324333190918,35.6259002685547}, {496.57861328125,47.7324333190918,24.3761005401611}, {511.07861328125,47.7324333190918,36.0009002685547}, {511.07861328125,47.7324333190918,24.0011005401611}, {514.70361328125,47.7324333190918,30.1885013580322}, {514.70361328125,47.7324333190918,35.8134994506836}, {514.70361328125,47.7324333190918,29.8135013580322}, {514.70361328125,47.7324333190918,24.1885013580322}, {496.57861328125,47.7324333190918,36.0009002685547}, {496.57861328125,47.7324333190918,24.0011005401611}, {510.238800048828,49.7324333190918,24.0011005401611}, {510.238800048828,49.7324333190918,36.0009002685547}, {514.70361328125,47.7324333190918,24.0011005401611}, {514.70361328125,47.7324333190918,36.0009002685547}, {496.158813476562,48.7324333190918,36.0009002685547}, {496.158813476562,48.7324333190918,24.0011005401611}, {502.808807373047,62.8258323669434,30.0011005401611}, {509.608795166016,51.2324333190918,24.0011005401611}, {509.608795166016,51.2324333190918,36.0009002685547}, {491.641204833984,50.8574333190918,24.0011005401611}, {495.423797607422,50.4824333190918,36.0009002685547}, {495.423797607422,50.4824333190918,24.0011005401611}, {491.641204833984,50.8574333190918,36.0009002685547}, {495.528594970703,50.2324333190918,24.0011005401611}, {492.0087890625,49.9824333190918,24.0011005401611}, {509.818786621094,50.7324333190918,24.0011005401611}, {495.948608398438,49.2324333190918,36.0009002685547}, {495.528594970703,50.2324333190918,36.0009002685547}, {495.948608398438,49.2324333190918,24.0011005401611}, {509.818786621094,50.7324333190918,36.0009002685547}, {492.0087890625,49.9824333190918,36.0009002685547}, {491.956207275391,50.1074333190918,24.0011005401611}, {491.956207275391,50.1074333190918,36.0009002685547}, {502.928588867188,81.599235534668,30.0011005401611}, {491.851013183594,50.3574333190918,36.0009002685547}, {491.851013183594,50.3574333190918,24.0011005401611}, {496.195404052734,54.7190322875977,30.0011005401611}, {509.361999511719,54.7190322875977,30.0011005401611}, {488.632598876953,51.7256317138672,30.0011005401611}, {488.632598876953,51.7256317138672,29.5091018676758}, {488.632598876953,51.7188339233398,24.0011005401611}, {488.632598876953,51.7256317138672,27.4929008483887}, {488.632598876953,51.7324333190918,30.0011005401611}, {488.632598876953,51.7324333190918,29.0175018310547}, {488.632598876953,51.7324333190918,24.9847011566162}, {488.632598876953,51.7324333190918,24.0011005401611}, {488.632598876953,51.7188339233398,30.0011005401611}, {488.632598876953,51.7176322937012,24.0011005401611}, {488.632598876953,51.7182312011719,30.0011005401611}, {488.632598876953,51.7176322937012,30.0011005401611}, {488.632598876953,51.715030670166,24.0011005401611}, {488.632598876953,51.7162322998047,30.0011005401611}, {488.632598876953,50.761833190918,24.0011005401611}, {488.632598876953,50.7578315734863,24.0011005401611}, {488.632598876953,50.7598342895508,30.0011005401611}, {488.632598876953,50.7522315979004,24.0011005401611}, {488.632598876953,49.7838325500488,24.0011005401611}, {488.632598876953,50.2680320739746,30.0011005401611}, {488.632598876953,51.7046318054199,24.0011005401611}, {488.632598876953,51.709831237793,30.0011005401611}, {488.632598876953,50.9120330810547,24.0011005401611}, {488.632598876953,50.8882331848145,24.0011005401611}, {488.632598876953,50.9002304077148,30.0011005401611}, {488.632598876953,47.7324333190918,24.0370998382568}, {488.632598876953,48.5612335205078,30.0011005401611}, {488.632598876953,47.7324333190918,24.0011005401611}, {488.632598876953,47.7324333190918,24.1091003417969}, {488.632598876953,48.5612335205078,30.0189018249512}, {488.632598876953,47.7324333190918,25.3211002349854}, {488.632598876953,48.5612335205078,30.0551013946533}, {488.632598876953,47.7324333190918,25.4651012420654}, {488.632598876953,48.5612335205078,30.6609001159668}, {488.632598876953,47.7324333190918,25.5371017456055}, {488.632598876953,48.5612335205078,30.7329006195068}, {488.632598876953,47.7324333190918,25.6091003417969}, {488.632598876953,48.5612335205078,30.7689018249512}, {488.632598876953,47.7324333190918,25.8971004486084}, {488.632598876953,48.5612335205078,30.8051013946533}, {488.632598876953,47.7324333190918,28.321102142334}, {488.632598876953,48.5612335205078,30.9491004943848}, {488.632598876953,47.7324333190918,28.4651012420654}, {488.632598876953,48.5612335205078,32.1609001159668}, {488.632598876953,47.7324333190918,28.5371017456055}, {488.632598876953,48.5612335205078,32.2329025268555}, {488.632598876953,47.7324333190918,28.6811008453369}, {488.632598876953,48.5612335205078,32.2689018249512}, {488.632598876953,47.7324333190918,31.1049003601074}, {488.632598876953,48.5612335205078,32.3411026000977}, {488.632598876953,47.7324333190918,31.3929004669189}, {488.632598876953,49.3900299072266,36.0009002685547}, {488.632598876953,47.7324333190918,31.536901473999}, {488.632598876953,47.7324333190918,31.6809005737305}, {488.632598876953,47.7324333190918,34.1049003601074}, {488.632598876953,47.7324333190918,34.3929023742676}, {488.632598876953,47.7324333190918,34.464900970459}, {488.632598876953,47.7324333190918,34.5369033813477}, {488.632598876953,47.7324333190918,34.6809005737305}, {488.632598876953,47.7324333190918,35.8929023742676}, {488.632598876953,47.7324333190918,35.964900970459}, {488.632598876953,47.7324333190918,36.0009002685547}, {488.632598876953,50.8816299438477,24.0011005401611}, {488.632598876953,50.8850326538086,30.0011005401611}, {488.632598876953,49.7480316162109,24.0011005401611}, {488.632598876953,49.7426300048828,24.0011005401611}, {488.632598876953,49.745231628418,30.0011005401611}, {488.632598876953,49.7592315673828,24.0011005401611}, {488.632598876953,49.7536315917969,30.0011005401611}, {488.632598876953,49.3900299072266,24.0011005401611}, {488.632598876953,49.5664329528809,30.0011005401611}, {488.632598876953,50.8786315917969,24.0011005401611}, {488.632598876953,50.7764320373535,24.0011005401611}, {488.632598876953,50.8274307250977,30.0011005401611}, {488.632598876953,50.7550315856934,30.0011005401611}, {488.632598876953,50.7692337036133,30.0011005401611}, {488.632598876953,50.9284324645996,24.0011005401611}, {488.632598876953,50.9202308654785,30.0011005401611}, {488.632598876953,51.1788330078125,24.0011005401611}, {488.632598876953,51.139232635498,24.0011005401611}, {488.632598876953,51.1590309143066,30.0011005401611}, {488.632598876953,51.2324333190918,24.0011005401611}, {488.632598876953,51.2056312561035,30.0011005401611}, {488.632598876953,51.4340324401855,24.0011005401611}, {488.632598876953,51.3946304321289,24.0011005401611}, {488.632598876953,51.4142303466797,30.0011005401611}, {488.632598876953,51.4498329162598,24.0011005401611}, {488.632598876953,51.5772323608398,30.0011005401611}, {488.632598876953,51.4418334960938,30.0011005401611}, {488.632598876953,51.3136329650879,30.0011005401611}, {488.632598876953,49.7714309692383,30.0011005401611}, {488.632598876953,51.0338325500488,30.0011005401611}, {488.632598876953,50.8816299438477,30.0011005401611}, {488.632598876953,50.8800315856934,30.0011005401611}, {488.632598876953,51.7188339233398,36.0009002685547}, {488.632598876953,51.7176322937012,36.0009002685547}, {488.632598876953,49.3900299072266,30.0011005401611}, {488.632598876953,50.7522315979004,30.0011005401611}, {488.632598876953,50.7522315979004,36.0009002685547}, {488.632598876953,49.7426300048828,30.0011005401611}, {488.632598876953,49.7426300048828,36.0009002685547}, {488.632598876953,49.7480316162109,30.0011005401611}, {488.632598876953,49.7480316162109,36.0009002685547}, {488.632598876953,51.715030670166,30.0011005401611}, {488.632598876953,51.715030670166,36.0009002685547}, {488.632598876953,50.7578315734863,30.0011005401611}, {488.632598876953,50.7578315734863,36.0009002685547}, {488.632598876953,50.761833190918,30.0011005401611}, {488.632598876953,50.761833190918,36.0009002685547}, {488.632598876953,50.8882331848145,30.0011005401611}, {488.632598876953,50.8882331848145,36.0009002685547}, {488.632598876953,49.7592315673828,30.0011005401611}, {488.632598876953,49.7592315673828,36.0009002685547}, {488.632598876953,51.1788330078125,30.0011005401611}, {488.632598876953,51.1788330078125,36.0009002685547}, {488.632598876953,50.9120330810547,30.0011005401611}, {488.632598876953,50.9120330810547,36.0009002685547}, {488.632598876953,51.4498329162598,30.0011005401611}, {488.632598876953,51.4498329162598,36.0009002685547}, {488.632598876953,51.7046318054199,30.0011005401611}, {488.632598876953,51.7046318054199,36.0009002685547}, {488.632598876953,51.2324333190918,30.0011005401611}, {488.632598876953,51.2324333190918,36.0009002685547}, {488.632598876953,51.3946304321289,30.0011005401611}, {488.632598876953,51.3946304321289,36.0009002685547}, {488.632598876953,51.4340324401855,30.0011005401611}, {488.632598876953,51.4340324401855,36.0009002685547}, {488.632598876953,49.7838325500488,30.0011005401611}, {488.632598876953,49.7838325500488,36.0009002685547}, {488.632598876953,50.7764320373535,30.0011005401611}, {488.632598876953,50.7764320373535,36.0009002685547}, {488.632598876953,51.139232635498,30.0011005401611}, {488.632598876953,51.139232635498,36.0009002685547}, {488.632598876953,50.9284324645996,30.0011005401611}, {488.632598876953,50.9284324645996,36.0009002685547}, {488.632598876953,50.8816299438477,36.0009002685547}, {488.632598876953,50.8786315917969,30.0011005401611}, {488.632598876953,50.8786315917969,36.0009002685547}, {488.632598876953,51.7324333190918,35.0173034667969}, {488.632598876953,51.7324333190918,36.0009002685547}, {488.632598876953,51.7324333190918,30.9847011566162}, {517.188415527344,51.7140884399414,24.0011005401611}, {517.188415527344,51.7140884399414,36.0009002685547}, {517.188415527344,50.4475173950195,24.0011005401611}, {517.188415527344,51.7324333190918,35.3734130859375}, {517.188415527344,51.7324333190918,36.0009002685547}, {517.188415527344,51.7324333190918,34.1185760498047}, {517.188415527344,51.7324333190918,31.88330078125}, {517.188415527344,51.7324333190918,30.0011005401611}, {517.188415527344,51.7324333190918,28.1187744140625}, {517.188415527344,51.7324333190918,25.8834266662598}, {517.188415527344,51.7324333190918,24.6285915374756}, {517.188415527344,51.7324333190918,24.0011005401611}, {517.188415527344,47.7324333190918,24.0600452423096}, {517.188415527344,47.7324333190918,24.0011005401611}, {517.188415527344,50.4475173950195,36.0009002685547}, {517.188415527344,47.7324333190918,24.1779975891113}, {517.188415527344,47.7324333190918,24.6498031616211}, {517.188415527344,47.7324333190918,28.7625770568848}, {517.188415527344,47.7324333190918,29.7061901092529}, {517.188415527344,47.7324333190918,29.9420928955078}, {517.188415527344,47.7324333190918,30.0600452423096}, {517.188415527344,47.7324333190918,30.2959480285645}, {517.188415527344,47.7324333190918,31.2395629882812}, {517.188415527344,47.7324333190918,35.3521995544434}, {517.188415527344,47.7324333190918,35.8240051269531}, {517.188415527344,47.7324333190918,35.9419555664062}, {517.188415527344,47.7324333190918,36.0009002685547} }, - { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}, {12,2,1}, {12,1,13}, {14,15,16}, {17,18,19}, {20,21,22}, {17,19,23}, {24,25,26}, {27,13,1}, {28,25,29}, {30,31,32}, {28,33,34}, {35,36,7}, {37,38,39}, {40,10,41}, {42,43,44}, {45,5,4}, {46,47,48}, {46,48,49}, {45,4,50}, {51,52,53}, {51,54,55}, {56,52,57}, {58,59,60}, {61,50,4}, {62,63,64}, {65,34,33}, {66,67,42}, {68,17,69}, {70,71,22}, {66,42,72}, {73,16,15}, {35,7,74}, {75,76,54}, {77,27,1}, {78,32,31}, {75,54,79}, {80,26,25}, {81,80,25}, {82,83,48}, {84,20,85}, {81,25,86}, {87,88,19}, {0,89,1}, {90,91,92}, {90,10,93}, {38,94,39}, {94,95,39}, {3,7,96}, {97,15,98}, {97,99,15}, {92,91,100}, {89,101,1}, {102,39,95}, {103,11,10}, {104,96,7}, {105,15,99}, {106,61,4}, {107,108,33}, {76,55,54}, {109,91,110}, {111,23,19}, {112,63,113}, {114,115,48}, {116,59,117}, {118,20,119}, {120,31,121}, {122,44,43}, {110,91,123}, {124,125,126}, {127,128,129}, {127,130,124}, {131,124,132}, {126,133,134}, {135,136,126}, {137,138,127}, {139,127,138}, {128,140,141}, {142,128,143}, {144,140,145}, {100,91,146}, {147,148,134}, {101,149,1}, {102,150,39}, {103,10,151}, {145,140,152}, {152,140,153}, {148,154,134}, {154,155,134}, {156,15,105}, {157,104,7}, {36,8,7}, {158,37,39}, {159,19,88}, {160,19,159}, {161,59,58}, {161,117,59}, {162,31,30}, {162,121,31}, {163,43,164}, {163,165,43}, {166,167,43}, {167,164,43}, {168,57,52}, {82,48,169}, {114,170,171}, {108,65,33}, {64,63,112}, {114,172,170}, {160,173,170}, {171,170,173}, {172,174,170}, {160,170,174}, {175,176,177}, {178,77,1}, {179,31,120}, {175,180,176}, {181,182,176}, {177,176,182}, {180,183,176}, {181,176,183}, {184,42,67}, {185,69,17}, {160,111,19}, {186,187,160}, {188,189,114}, {190,188,114}, {114,48,191}, {192,114,193}, {194,160,195}, {196,160,194}, {197,198,181}, {199,197,181}, {122,43,165}, {200,201,175}, {202,175,203}, {204,175,202}, {205,119,20}, {206,181,207}, {208,209,15}, {210,15,209}, {211,10,9}, {212,10,211}, {213,214,215}, {216,217,218}, {219,14,17}, {113,63,220}, {221,222,48}, {191,48,222}, {22,223,20}, {205,20,223}, {224,40,42}, {123,91,225}, {214,226,215}, {227,215,226}, {218,217,228}, {229,228,217}, {215,230,213}, {125,135,126}, {217,216,231}, {129,128,142}, {216,213,232}, {130,132,124}, {213,216,233}, {234,213,235}, {236,227,237}, {238,237,227}, {239,240,216}, {233,216,240}, {241,242,229}, {243,229,242}, {215,227,244}, {245,215,246}, {217,247,229}, {248,249,217}, {232,213,250}, {230,250,213}, {133,147,134}, {244,227,251}, {236,252,227}, {251,227,252}, {231,216,253}, {254,253,216}, {141,140,144}, {247,255,229}, {241,229,256}, {255,256,229}, {257,241,258}, {259,146,91}, {260,261,236}, {262,1,149}, {263,264,241}, {265,241,264}, {266,236,267}, {268,267,236}, {49,48,83}, {166,43,269}, {270,271,272}, {273,274,275}, {276,274,277}, {278,151,10}, {279,280,272}, {281,39,150}, {272,282,279}, {155,283,134}, {274,276,284}, {153,140,285}, {286,276,287}, {265,276,286}, {288,289,279}, {268,288,279}, {290,291,272}, {271,290,272}, {292,274,293}, {275,274,292}, {294,265,295}, {276,265,294}, {296,297,268}, {279,296,268}, {241,265,298}, {298,265,299}, {236,300,268}, {300,301,268}, {107,33,78}, {302,303,59}, {304,305,279}, {282,304,279}, {306,276,307}, {284,276,306}, {185,17,73}, {308,309,221}, {158,39,70}, {310,41,10}, {15,311,208}, {7,6,312}, {313,314,6}, {315,6,314}, {316,208,317}, {318,317,208}, {258,241,319}, {319,241,320}, {261,321,236}, {321,322,236}, {6,315,323}, {208,324,318}, {270,325,318}, {326,318,325}, {327,328,315}, {273,315,328}, {118,329,20}, {330,20,329}, {331,332,25}, {86,25,332}, {333,334,52}, {335,52,334}, {115,336,48}, {169,48,336}, {62,106,4}, {35,15,210}, {35,337,15}, {158,10,212}, {158,310,10}, {338,178,1}, {339,59,116}, {107,302,59}, {66,22,340}, {66,341,22}, {185,221,342}, {185,308,221}, {75,31,179}, {75,343,31}, {166,20,330}, {166,85,20}, {81,52,335}, {81,168,52}, {82,19,344}, {82,87,19}, {108,339,345}, {346,108,345}, {64,347,348}, {349,347,64}, {178,109,350}, {351,178,350}, {179,352,353}, {354,352,179}, {355,208,356}, {356,208,311}, {357,358,6}, {358,312,6}, {68,22,21}, {68,340,22}, {221,48,47}, {184,342,221}, {359,270,360}, {318,360,270}, {361,362,273}, {315,273,362}, {272,102,270}, {363,270,102}, {274,273,103}, {364,103,273}, {21,19,18}, {21,20,84}, {184,46,42}, {43,42,46}, {12,22,71}, {365,22,12}, {14,98,15}, {14,220,63}, {40,93,10}, {40,225,91}, {45,221,309}, {366,221,45}, {313,367,212}, {212,367,368}, {36,369,367}, {313,36,367}, {316,37,367}, {37,368,367}, {210,367,369}, {316,367,210}, {362,370,315}, {370,323,315}, {360,318,371}, {371,318,324}, {372,331,159}, {159,195,160}, {373,115,56}, {115,114,189}, {52,56,161}, {374,161,56}, {25,28,331}, {375,331,28}, {376,333,163}, {163,203,175}, {377,118,24}, {118,181,198}, {25,24,162}, {378,162,24}, {52,51,333}, {379,333,51}, {167,380,381}, {376,167,381}, {377,381,330}, {330,381,380}, {335,381,382}, {376,381,335}, {373,383,169}, {169,383,384}, {168,385,383}, {373,168,383}, {372,87,383}, {87,384,383}, {377,80,381}, {80,382,381}, {86,383,385}, {372,383,86}, {106,348,347}, {386,106,347}, {375,65,346}, {108,346,65}, {64,112,349}, {387,349,112}, {171,190,114}, {346,345,171}, {374,190,345}, {171,345,190}, {349,172,347}, {172,114,192}, {386,347,192}, {172,192,347}, {173,160,196}, {171,173,346}, {375,346,196}, {173,196,346}, {172,349,174}, {174,186,160}, {387,186,349}, {174,349,186}, {64,348,62}, {106,62,348}, {108,107,339}, {59,339,107}, {374,345,116}, {339,116,345}, {76,353,352}, {379,76,352}, {388,77,351}, {178,351,77}, {179,120,354}, {378,354,120}, {177,200,175}, {351,350,177}, {389,200,350}, {177,350,200}, {354,180,352}, {180,175,204}, {379,352,204}, {180,204,352}, {182,181,206}, {177,182,351}, {388,351,206}, {182,206,351}, {180,354,183}, {183,199,181}, {378,199,354}, {183,354,199}, {91,109,338}, {178,338,109}, {76,75,353}, {179,353,75}, {389,350,110}, {109,110,350}, {390,391,392}, {393,394,395}, {224,122,389}, {122,175,201}, {365,388,205}, {205,207,181}, {66,340,396}, {68,396,340}, {184,396,342}, {185,342,396}, {66,396,67}, {184,67,396}, {68,69,396}, {185,396,69}, {219,111,387}, {111,160,187}, {366,386,191}, {191,193,114}, {150,272,280}, {102,272,150}, {151,277,274}, {103,151,274}, {161,374,117}, {116,117,374}, {366,61,386}, {106,386,61}, {111,187,387}, {186,387,187}, {56,188,374}, {190,374,188}, {191,386,193}, {192,193,386}, {331,375,194}, {196,194,375}, {28,34,375}, {65,375,34}, {219,387,113}, {112,113,387}, {224,389,123}, {110,123,389}, {51,55,379}, {76,379,55}, {24,197,378}, {199,378,197}, {122,201,389}, {200,389,201}, {333,379,202}, {204,202,379}, {205,388,207}, {206,207,388}, {365,27,388}, {77,388,27}, {162,378,121}, {120,121,378}, {162,30,25}, {30,29,25}, {51,53,54}, {303,60,59}, {28,29,33}, {29,397,33}, {161,58,52}, {53,52,58}, {21,84,19}, {84,344,19}, {46,49,43}, {49,269,43}, {208,316,209}, {210,209,316}, {327,313,211}, {212,211,313}, {36,35,369}, {210,369,35}, {37,158,368}, {212,368,158}, {6,8,313}, {36,313,8}, {326,38,316}, {37,316,38}, {392,391,398}, {399,398,391}, {394,400,395}, {401,395,400}, {390,214,391}, {214,213,234}, {393,395,218}, {218,239,216}, {402,230,403}, {230,215,245}, {125,124,131}, {404,125,403}, {405,406,231}, {231,248,217}, {129,137,127}, {407,406,129}, {130,127,139}, {402,130,408}, {194,195,331}, {159,331,195}, {115,189,56}, {188,56,189}, {14,219,220}, {113,220,219}, {45,50,366}, {61,366,50}, {221,366,222}, {191,222,366}, {17,23,219}, {111,219,23}, {118,198,24}, {197,24,198}, {202,203,333}, {163,333,203}, {40,224,225}, {123,225,224}, {12,13,365}, {27,365,13}, {22,365,223}, {205,223,365}, {42,44,224}, {122,224,44}, {399,391,234}, {214,234,391}, {401,239,395}, {218,395,239}, {214,390,226}, {226,238,227}, {218,228,393}, {228,229,243}, {401,399,233}, {233,235,213}, {392,409,390}, {410,390,409}, {394,393,411}, {412,411,393}, {402,403,131}, {125,131,403}, {405,137,406}, {129,406,137}, {405,408,139}, {130,139,408}, {230,245,403}, {404,403,245}, {231,406,248}, {407,248,406}, {232,254,216}, {402,408,232}, {413,404,244}, {244,246,215}, {414,247,407}, {247,217,249}, {133,126,136}, {415,133,413}, {141,143,128}, {416,414,141}, {410,238,390}, {226,390,238}, {412,393,243}, {228,243,393}, {233,399,235}, {234,235,399}, {237,260,236}, {238,410,237}, {417,260,410}, {237,410,260}, {239,401,240}, {233,240,401}, {242,241,257}, {243,242,412}, {418,412,257}, {242,257,412}, {401,419,399}, {398,399,419}, {417,410,420}, {409,420,410}, {400,421,401}, {419,401,421}, {418,422,412}, {411,412,422}, {413,135,404}, {125,404,135}, {414,407,142}, {129,142,407}, {130,402,132}, {131,132,402}, {133,136,413}, {135,413,136}, {423,147,415}, {133,415,147}, {137,405,138}, {139,138,405}, {141,414,143}, {142,143,414}, {424,416,144}, {141,144,416}, {405,254,408}, {232,408,254}, {244,404,246}, {245,246,404}, {247,249,407}, {248,407,249}, {232,250,402}, {230,402,250}, {415,413,251}, {244,251,413}, {252,236,266}, {251,252,415}, {423,415,266}, {252,266,415}, {231,253,405}, {254,405,253}, {416,255,414}, {247,414,255}, {256,263,241}, {255,416,256}, {424,263,416}, {256,416,263}, {257,258,418}, {425,418,258}, {260,417,261}, {426,261,417}, {422,418,427}, {427,259,91}, {420,428,417}, {428,1,262}, {147,423,148}, {429,148,423}, {263,424,264}, {264,295,265}, {266,267,423}, {267,268,297}, {144,145,424}, {430,424,145}, {49,431,269}, {166,269,431}, {82,431,83}, {49,83,431}, {84,85,431}, {166,431,85}, {82,344,431}, {84,431,344}, {432,278,90}, {10,90,278}, {433,0,281}, {39,281,0}, {362,361,434}, {435,271,359}, {270,359,271}, {436,361,275}, {273,275,361}, {360,437,359}, {277,287,276}, {151,278,277}, {280,279,289}, {150,280,281}, {436,438,439}, {439,285,140}, {90,92,432}, {440,432,92}, {282,272,291}, {441,282,442}, {284,293,274}, {443,438,284}, {278,432,286}, {286,299,265}, {281,288,433}, {288,268,301}, {0,433,89}, {444,89,433}, {435,445,442}, {445,134,283}, {439,446,436}, {361,436,446}, {442,290,435}, {271,435,290}, {438,436,292}, {275,292,436}, {445,435,447}, {359,447,435}, {286,287,278}, {277,278,287}, {288,281,289}, {280,289,281}, {145,152,430}, {443,430,152}, {148,429,154}, {441,154,429}, {424,430,294}, {294,307,276}, {423,296,429}, {296,279,305}, {425,440,100}, {92,100,440}, {290,442,291}, {282,291,442}, {292,293,438}, {284,438,293}, {298,320,241}, {432,440,298}, {300,236,322}, {433,300,444}, {426,101,444}, {89,444,101}, {107,448,302}, {302,79,54}, {78,31,343}, {107,78,448}, {75,79,448}, {302,448,79}, {78,343,448}, {75,448,343}, {427,418,259}, {425,259,418}, {428,262,417}, {426,417,262}, {437,449,359}, {447,359,449}, {434,361,450}, {446,450,361}, {32,33,397}, {78,33,32}, {53,303,54}, {302,54,303}, {152,153,443}, {438,443,153}, {429,304,441}, {282,441,304}, {430,443,306}, {284,306,443}, {154,441,155}, {442,155,441}, {298,299,432}, {286,432,299}, {300,433,301}, {288,301,433}, {185,451,308}, {308,74,7}, {73,15,337}, {185,73,451}, {35,74,451}, {308,451,74}, {73,337,451}, {35,451,337}, {158,452,310}, {310,72,42}, {70,22,341}, {158,70,452}, {66,72,452}, {310,452,72}, {70,341,452}, {66,452,341}, {313,327,314}, {315,314,327}, {316,317,326}, {318,326,317}, {15,156,311}, {356,311,156}, {7,312,157}, {358,157,312}, {211,9,327}, {364,327,9}, {38,326,94}, {363,94,326}, {294,295,424}, {264,424,295}, {296,423,297}, {267,297,423}, {262,149,426}, {101,426,149}, {258,319,425}, {440,425,319}, {261,426,321}, {444,321,426}, {259,425,146}, {100,146,425}, {306,307,430}, {294,430,307}, {304,429,305}, {296,305,429}, {319,320,440}, {298,440,320}, {321,444,322}, {300,322,444}, {445,283,442}, {155,442,283}, {439,438,285}, {153,285,438}, {17,68,18}, {21,18,68}, {46,184,47}, {221,47,184}, {102,95,363}, {94,363,95}, {9,11,364}, {103,364,11}, {6,323,357}, {370,357,323}, {371,324,355}, {208,355,324}, {270,363,325}, {326,325,363}, {327,364,328}, {273,328,364}, {0,2,39}, {12,39,2}, {90,93,91}, {40,91,93}, {14,16,17}, {73,17,16}, {45,309,7}, {308,7,309}, {12,71,39}, {70,39,71}, {40,41,42}, {310,42,41}, {97,98,63}, {14,63,98}, {3,5,7}, {45,7,5}, {118,377,329}, {330,329,377}, {331,372,332}, {86,332,372}, {333,376,334}, {335,334,376}, {115,373,336}, {169,336,373}, {167,166,380}, {330,380,166}, {80,81,382}, {335,382,81}, {86,385,81}, {168,81,385}, {169,384,82}, {87,82,384}, {159,88,372}, {87,372,88}, {163,164,376}, {167,376,164}, {24,26,377}, {80,377,26}, {56,57,373}, {168,373,57}, {32,397,30}, {29,30,397}, {58,60,53}, {303,53,60}, {205,181,119}, {118,119,181}, {163,175,165}, {122,165,175}, {453,454,455}, {454,456,455}, {457,455,456}, {458,455,457}, {459,455,458}, {460,455,459}, {461,462,463}, {464,465,466}, {467,468,469}, {470,471,472}, {465,473,474}, {475,476,477}, {478,479,480}, {481,482,478}, {483,484,481}, {485,486,483}, {487,488,485}, {489,490,487}, {491,492,489}, {493,494,491}, {495,496,493}, {497,498,495}, {499,500,497}, {501,502,499}, {503,504,501}, {505,504,503}, {506,504,505}, {507,504,506}, {508,504,507}, {509,504,508}, {510,504,509}, {511,504,510}, {512,504,511}, {513,504,512}, {514,504,513}, {476,515,516}, {517,518,519}, {520,517,521}, {518,522,523}, {522,480,479}, {524,525,526}, {468,470,527}, {525,467,528}, {529,475,530}, {531,532,533}, {534,531,535}, {536,537,538}, {473,539,540}, {539,536,541}, {537,534,542}, {471,520,543}, {532,529,544}, {545,524,546}, {453,461,547}, {463,464,548}, {523,549,504}, {527,550,551}, {519,552,553}, {521,554,555}, {466,556,557}, {469,558,559}, {528,560,561}, {477,562,563}, {543,564,565}, {535,566,567}, {530,568,569}, {540,570,571}, {474,572,573}, {542,574,575}, {538,576,577}, {541,578,579}, {472,580,581}, {526,582,583}, {533,584,585}, {544,586,587}, {516,545,588}, {588,589,590}, {455,460,4}, {591,592,63}, {462,455,4}, {592,547,63}, {547,548,63}, {465,462,4}, {548,557,63}, {127,124,501}, {127,501,499}, {505,503,124}, {124,126,507}, {124,507,506}, {509,508,126}, {126,134,512}, {126,512,511}, {510,509,126}, {128,127,493}, {128,493,491}, {497,495,127}, {489,487,128}, {140,128,483}, {140,483,481}, {487,485,128}, {478,480,140}, {480,522,140}, {514,513,134}, {504,514,134}, {551,581,437}, {471,470,434}, {445,447,555}, {445,555,553}, {134,445,553}, {134,553,504}, {446,439,518}, {446,518,517}, {439,140,522}, {439,522,518}, {515,476,358}, {563,588,356}, {557,573,63}, {473,465,4}, {437,360,559}, {437,559,551}, {360,371,561}, {360,561,559}, {362,434,470}, {362,470,468}, {370,362,468}, {370,468,467}, {499,497,127}, {506,505,124}, {495,493,127}, {513,512,134}, {481,478,140}, {447,449,565}, {447,565,555}, {450,446,517}, {450,517,520}, {356,156,569}, {356,569,563}, {157,358,476}, {157,476,475}, {357,370,467}, {357,467,525}, {371,355,583}, {371,583,561}, {460,459,4}, {63,62,593}, {63,593,591}, {62,4,459}, {62,459,458}, {532,531,104}, {531,534,104}, {567,585,105}, {575,567,105}, {4,3,539}, {4,539,473}, {536,539,3}, {97,63,573}, {97,573,571}, {571,579,97}, {99,97,579}, {99,579,577}, {105,99,577}, {105,577,575}, {96,104,534}, {96,534,537}, {3,96,537}, {3,537,536}, {503,501,124}, {508,507,126}, {491,489,128}, {511,510,126}, {485,483,128}, {434,450,520}, {434,520,471}, {449,437,581}, {449,581,565}, {156,105,585}, {156,585,587}, {587,569,156}, {104,157,529}, {104,529,532}, {475,529,157}, {590,583,355}, {355,356,588}, {355,588,590}, {358,357,524}, {358,524,515}, {525,524,357}, {458,457,62}, {457,593,62}, {479,478,482}, {479,504,549}, {479,482,504}, {482,481,484}, {472,551,550}, {581,551,472}, {482,484,504}, {484,483,486}, {523,553,552}, {504,553,523}, {540,573,572}, {571,573,540}, {544,585,584}, {587,585,544}, {542,577,576}, {575,577,542}, {526,590,589}, {583,590,526}, {535,575,574}, {567,575,535}, {533,567,566}, {585,567,533}, {538,579,578}, {577,579,538}, {543,581,580}, {565,581,543}, {477,569,568}, {563,569,477}, {530,587,586}, {569,587,530}, {541,571,570}, {579,571,541}, {528,583,582}, {561,583,528}, {591,453,592}, {547,592,453}, {521,565,564}, {555,565,521}, {474,557,556}, {573,557,474}, {516,563,562}, {588,563,516}, {519,555,554}, {553,555,519}, {527,559,558}, {551,559,527}, {469,561,560}, {559,561,469}, {462,461,455}, {453,455,461}, {461,463,547}, {548,547,463}, {465,464,462}, {463,462,464}, {464,466,548}, {557,548,466}, {469,560,467}, {528,467,560}, {472,550,470}, {527,470,550}, {474,556,465}, {466,465,556}, {477,568,475}, {530,475,568}, {516,562,476}, {477,476,562}, {519,554,517}, {521,517,554}, {521,564,520}, {543,520,564}, {523,552,518}, {519,518,552}, {479,549,522}, {523,522,549}, {526,589,524}, {589,546,524}, {527,558,468}, {469,468,558}, {528,582,525}, {526,525,582}, {530,586,529}, {544,529,586}, {533,566,531}, {535,531,566}, {535,574,534}, {542,534,574}, {538,578,536}, {541,536,578}, {540,572,473}, {474,473,572}, {541,570,539}, {540,539,570}, {542,576,537}, {538,537,576}, {543,580,471}, {472,471,580}, {544,584,532}, {533,532,584}, {524,545,515}, {516,515,545}, {545,546,588}, {589,588,546}, {453,591,454}, {593,454,591}, {484,486,504}, {486,485,488}, {486,488,504}, {488,487,490}, {488,490,504}, {490,489,492}, {490,492,504}, {492,491,494}, {492,494,504}, {494,493,496}, {494,496,504}, {496,495,498}, {496,498,504}, {498,497,500}, {498,500,504}, {500,499,502}, {500,502,504}, {501,504,502}, {454,593,456}, {457,456,593}, {594,595,596}, {597,598,594}, {599,597,594}, {600,599,594}, {601,600,594}, {602,601,594}, {603,602,594}, {604,603,594}, {605,604,594}, {606,607,608}, {609,606,608}, {610,609,608}, {611,610,608}, {612,611,608}, {613,612,608}, {614,613,608}, {615,614,608}, {616,615,608}, {617,616,608}, {618,617,608}, {619,618,608}, {620,619,608}, {596,608,607}, {595,594,598}, {608,596,595}, {605,594,91}, {91,338,602}, {91,602,603}, {598,597,1}, {594,596,91}, {608,595,1}, {595,598,1}, {616,617,392}, {610,611,394}, {419,421,613}, {419,613,614}, {422,427,607}, {422,607,606}, {427,91,596}, {427,596,607}, {428,420,619}, {428,619,620}, {1,428,620}, {1,620,608}, {420,409,618}, {420,618,619}, {411,422,606}, {411,606,609}, {398,419,614}, {398,614,615}, {421,400,612}, {421,612,613}, {409,392,617}, {409,617,618}, {394,411,609}, {394,609,610}, {604,605,91}, {338,1,599}, {338,599,600}, {392,398,615}, {392,615,616}, {400,394,611}, {400,611,612}, {603,604,91}, {601,602,338}, {597,599,1}, {600,601,338} }); + { {513.075988769531f,51.6074333190918f,36.0009002685547f}, {516.648803710938f,51.7324333190918f,36.0009002685547f}, {513.495178222656f,51.7324333190918f,36.0009002685547f}, {489.391204833984f,51.4824333190918f,24.0011005401611f}, {488.928588867188f,51.7324333190918f,24.0011005401611f}, {492.06201171875f,51.7324333190918f,24.0011005401611f}, {496.840393066406f,51.2324333190918f,24.0011005401611f}, {495.195404052734f,51.7324333190918f,24.0011005401611f}, {498.981994628906f,51.7324333190918f,24.0011005401611f}, {506.966613769531f,51.6074333190918f,24.0011005401611f}, {510.342010498047f,51.7324333190918f,24.0011005401611f}, {507.163818359375f,51.6074333190918f,24.0011005401611f}, {512.515380859375f,54.7190322875977f,36.0009002685547f}, {514.161987304688f,54.5058326721191f,36.0009002685547f}, {493.06201171875f,54.7190322875977f,36.0009002685547f}, {495.195404052734f,51.7324333190918f,36.0009002685547f}, {496.195404052734f,54.7190322875977f,36.0009002685547f}, {497.195404052734f,57.7058334350586f,36.0009002685547f}, {500.851989746094f,60.2658309936523f,36.0009002685547f}, {498.915405273438f,62.8258323669434f,36.0009002685547f}, {506.701995849609f,62.8258323669434f,36.0009002685547f}, {503.648590087891f,60.2658309936523f,36.0009002685547f}, {508.381805419922f,57.7058334350586f,36.0009002685547f}, {496.418792724609f,60.052433013916f,36.0009002685547f}, {506.515197753906f,72.2124328613281f,36.0009002685547f}, {502.808807373047f,74.5324325561523f,36.0009002685547f}, {503.781982421875f,71.6058349609375f,36.0009002685547f}, {515.358764648438f,55.4658317565918f,36.0009002685547f}, {499.375183105469f,76.9058380126953f,36.0009002685547f}, {501.168792724609f,78.0658340454102f,36.0009002685547f}, {504.568786621094f,78.0658340454102f,36.0009002685547f}, {506.32861328125f,81.599235534668f,36.0009002685547f}, {502.928588867188f,81.599235534668f,36.0009002685547f}, {499.528594970703f,81.599235534668f,36.0009002685547f}, {498.20361328125f,77.8658294677734f,36.0009002685547f}, {495.195404052734f,51.7324333190918f,30.0011005401611f}, {498.981994628906f,51.7324333190918f,27.0011005401611f}, {506.555206298828f,51.7324333190918f,33.0009002685547f}, {506.555206298828f,51.7324333190918f,36.0009002685547f}, {510.342010498047f,51.7324333190918f,36.0009002685547f}, {512.515380859375f,54.7190322875977f,24.0011005401611f}, {509.361999511719f,54.7190322875977f,24.0011005401611f}, {508.381805419922f,57.7058334350586f,24.0011005401611f}, {506.701995849609f,62.8258323669434f,24.0011005401611f}, {509.188812255859f,60.052433013916f,24.0011005401611f}, {493.06201171875f,54.7190322875977f,24.0011005401611f}, {503.648590087891f,60.2658309936523f,24.0011005401611f}, {500.851989746094f,60.2658309936523f,24.0011005401611f}, {498.915405273438f,62.8258323669434f,24.0011005401611f}, {502.808807373047f,62.8258323669434f,24.0011005401611f}, {491.425201416016f,54.5058326721191f,24.0011005401611f}, {506.421813964844f,76.9058380126953f,24.0011005401611f}, {502.808807373047f,74.5324325561523f,24.0011005401611f}, {504.568786621094f,78.0658340454102f,24.0011005401611f}, {506.32861328125f,81.599235534668f,24.0011005401611f}, {507.618804931641f,77.8658294677734f,24.0011005401611f}, {499.221801757812f,72.2124328613281f,24.0011005401611f}, {501.835388183594f,71.6058349609375f,24.0011005401611f}, {501.168792724609f,78.0658340454102f,24.0011005401611f}, {499.528594970703f,81.599235534668f,24.0011005401611f}, {502.048583984375f,79.8324356079102f,24.0011005401611f}, {490.253601074219f,55.4658317565918f,24.0011005401611f}, {488.928588867188f,51.7324333190918f,30.0011005401611f}, {488.928588867188f,51.7324333190918f,36.0009002685547f}, {490.253601074219f,55.4658317565918f,31.5009002685547f}, {498.20361328125f,77.8658294677734f,34.5009002685547f}, {508.381805419922f,57.7058334350586f,30.0011005401611f}, {505.585388183594f,57.7058334350586f,27.0011005401611f}, {502.788818359375f,57.7058334350586f,36.0009002685547f}, {499.992004394531f,57.7058334350586f,33.0009002685547f}, {509.851989746094f,53.2258338928223f,33.0009002685547f}, {509.361999511719f,54.7190322875977f,36.0009002685547f}, {508.871795654297f,56.2124328613281f,27.0011005401611f}, {496.695404052734f,56.2124328613281f,33.0009002685547f}, {495.695404052734f,53.2258338928223f,27.0011005401611f}, {506.32861328125f,81.599235534668f,30.0011005401611f}, {507.618804931641f,77.8658294677734f,25.5011005401611f}, {515.358764648438f,55.4658317565918f,34.5009002685547f}, {501.228607177734f,81.599235534668f,33.0009002685547f}, {504.628601074219f,81.599235534668f,27.0011005401611f}, {503.781982421875f,71.6058349609375f,33.0009002685547f}, {502.808807373047f,74.5324325561523f,30.0011005401611f}, {498.915405273438f,62.8258323669434f,30.0011005401611f}, {500.861999511719f,62.8258323669434f,27.0011005401611f}, {502.808807373047f,62.8258323669434f,36.0009002685547f}, {504.755187988281f,62.8258323669434f,33.0009002685547f}, {501.835388183594f,71.6058349609375f,33.0009002685547f}, {499.888793945312f,65.7524337768555f,33.0009002685547f}, {499.888793945312f,65.7524337768555f,36.0009002685547f}, {513.128601074219f,51.4824333190918f,36.0009002685547f}, {513.075988769531f,51.6074333190918f,24.0011005401611f}, {516.648803710938f,51.7324333190918f,24.0011005401611f}, {513.128601074219f,51.4824333190918f,24.0011005401611f}, {513.495178222656f,51.7324333190918f,24.0011005401611f}, {506.966613769531f,51.6074333190918f,36.0009002685547f}, {507.163818359375f,51.6074333190918f,36.0009002685547f}, {490.337799072266f,51.4824333190918f,24.0011005401611f}, {489.391204833984f,51.4824333190918f,36.0009002685547f}, {492.06201171875f,51.7324333190918f,36.0009002685547f}, {490.337799072266f,51.4824333190918f,36.0009002685547f}, {513.233764648438f,51.2324333190918f,24.0011005401611f}, {513.233764648438f,51.2324333190918f,36.0009002685547f}, {504.773803710938f,51.4824333190918f,36.0009002685547f}, {504.773803710938f,51.4824333190918f,24.0011005401611f}, {489.266998291016f,51.2324333190918f,24.0011005401611f}, {489.266998291016f,51.2324333190918f,36.0009002685547f}, {490.253601074219f,55.4658317565918f,25.5011005401611f}, {499.528594970703f,81.599235534668f,30.0011005401611f}, {498.20361328125f,77.8658294677734f,31.5009002685547f}, {515.358764648438f,55.4658317565918f,28.5011005401611f}, {515.358764648438f,55.4658317565918f,25.5011005401611f}, {495.246795654297f,61.0124320983887f,36.0009002685547f}, {490.253601074219f,55.4658317565918f,34.5009002685547f}, {490.253601074219f,55.4658317565918f,36.0009002685547f}, {494.228607177734f,66.6658325195312f,24.0011005401611f}, {499.068786621094f,67.5192337036133f,24.0011005401611f}, {498.20361328125f,77.8658294677734f,25.5011005401611f}, {498.20361328125f,77.8658294677734f,24.0011005401611f}, {506.608795166016f,67.5192337036133f,36.0009002685547f}, {509.09521484375f,64.7458343505859f,36.0009002685547f}, {507.618804931641f,77.8658294677734f,34.5009002685547f}, {507.618804931641f,77.8658294677734f,36.0009002685547f}, {510.385406494141f,61.0124320983887f,24.0011005401611f}, {515.358764648438f,55.4658317565918f,24.0011005401611f}, {489.32861328125f,47.7324333190918f,31.5009002685547f}, {492.95361328125f,47.7324333190918f,33.5634994506836f}, {489.32861328125f,47.7324333190918f,34.5009002685547f}, {489.32861328125f,47.7324333190918f,28.5011005401611f}, {489.32861328125f,47.7324333190918f,25.5011005401611f}, {492.95361328125f,47.7324333190918f,26.4385013580322f}, {492.95361328125f,47.7324333190918f,30.5635013580322f}, {492.95361328125f,47.7324333190918f,32.0634994506836f}, {492.95361328125f,47.7324333190918f,31.3135013580322f}, {492.95361328125f,47.7324333190918f,35.4384994506836f}, {489.32861328125f,47.7324333190918f,36.0009002685547f}, {492.95361328125f,47.7324333190918f,34.3134994506836f}, {492.95361328125f,47.7324333190918f,34.6884994506836f}, {492.95361328125f,47.7324333190918f,27.9385013580322f}, {492.95361328125f,47.7324333190918f,28.6885013580322f}, {492.95361328125f,47.7324333190918f,29.0635013580322f}, {489.32861328125f,47.7324333190918f,24.0011005401611f}, {492.95361328125f,47.7324333190918f,24.5635013580322f}, {492.95361328125f,47.7324333190918f,25.6885013580322f}, {492.95361328125f,47.7324333190918f,25.3135013580322f}, {492.95361328125f,47.7324333190918f,24.1885013580322f}, {492.95361328125f,47.7324333190918f,24.0011005401611f}, {513.443786621094f,50.7324333190918f,24.0011005401611f}, {492.95361328125f,47.7324333190918f,35.8134994506836f}, {492.95361328125f,47.7324333190918f,36.0009002685547f}, {513.443786621094f,50.7324333190918f,36.0009002685547f}, {506.350402832031f,51.4824333190918f,36.0009002685547f}, {506.350402832031f,51.4824333190918f,24.0011005401611f}, {492.743804931641f,48.2324333190918f,24.0011005401611f}, {492.638793945312f,48.4824333190918f,24.0011005401611f}, {492.743804931641f,48.2324333190918f,36.0009002685547f}, {492.638793945312f,48.4824333190918f,36.0009002685547f}, {490.089599609375f,50.9824333190918f,36.0009002685547f}, {490.089599609375f,50.9824333190918f,24.0011005401611f}, {510.342010498047f,51.7324333190918f,30.0011005401611f}, {499.068786621094f,67.5192337036133f,36.0009002685547f}, {494.228607177734f,66.6658325195312f,36.0009002685547f}, {499.375183105469f,76.9058380126953f,24.0011005401611f}, {506.421813964844f,76.9058380126953f,36.0009002685547f}, {506.608795166016f,67.5192337036133f,24.0011005401611f}, {505.728607177734f,65.7524337768555f,24.0011005401611f}, {509.09521484375f,64.7458343505859f,24.0011005401611f}, {506.701995849609f,62.8258323669434f,30.0011005401611f}, {505.728607177734f,65.7524337768555f,27.0011005401611f}, {501.835388183594f,71.6058349609375f,27.0011005401611f}, {499.888793945312f,65.7524337768555f,27.0011005401611f}, {494.228607177734f,66.6658325195312f,30.0011005401611f}, {495.553588867188f,70.3992309570312f,28.5011005401611f}, {492.903594970703f,62.9324340820312f,28.5011005401611f}, {495.553588867188f,70.3992309570312f,31.5009002685547f}, {492.903594970703f,62.9324340820312f,31.5009002685547f}, {511.488800048828f,66.6658325195312f,24.0011005401611f}, {511.488800048828f,66.6658325195312f,30.0011005401611f}, {512.778564453125f,62.9324340820312f,28.5011005401611f}, {515.358764648438f,55.4658317565918f,31.5009002685547f}, {507.618804931641f,77.8658294677734f,31.5009002685547f}, {510.198791503906f,70.3992309570312f,28.5011005401611f}, {511.488800048828f,66.6658325195312f,36.0009002685547f}, {512.778564453125f,62.9324340820312f,31.5009002685547f}, {510.198791503906f,70.3992309570312f,31.5009002685547f}, {502.788818359375f,57.7058334350586f,24.0011005401611f}, {497.195404052734f,57.7058334350586f,30.0011005401611f}, {492.903594970703f,62.9324340820312f,34.5009002685547f}, {492.903594970703f,62.9324340820312f,36.0009002685547f}, {495.553588867188f,70.3992309570312f,24.0011005401611f}, {496.725189208984f,69.4392318725586f,24.0011005401611f}, {495.553588867188f,70.3992309570312f,25.5011005401611f}, {495.246795654297f,61.0124320983887f,24.0011005401611f}, {492.903594970703f,62.9324340820312f,25.5011005401611f}, {492.903594970703f,62.9324340820312f,24.0011005401611f}, {495.553588867188f,70.3992309570312f,36.0009002685547f}, {496.725189208984f,69.4392318725586f,36.0009002685547f}, {495.553588867188f,70.3992309570312f,34.5009002685547f}, {510.198791503906f,70.3992309570312f,36.0009002685547f}, {509.002014160156f,69.4392318725586f,36.0009002685547f}, {510.198791503906f,70.3992309570312f,34.5009002685547f}, {512.778564453125f,62.9324340820312f,25.5011005401611f}, {512.778564453125f,62.9324340820312f,24.0011005401611f}, {510.198791503906f,70.3992309570312f,24.0011005401611f}, {509.002014160156f,69.4392318725586f,24.0011005401611f}, {510.198791503906f,70.3992309570312f,25.5011005401611f}, {510.385406494141f,61.0124320983887f,36.0009002685547f}, {512.778564453125f,62.9324340820312f,34.5009002685547f}, {512.778564453125f,62.9324340820312f,36.0009002685547f}, {496.840393066406f,51.2324333190918f,36.0009002685547f}, {498.981994628906f,51.7324333190918f,36.0009002685547f}, {498.981994628906f,51.7324333190918f,33.0009002685547f}, {506.555206298828f,51.7324333190918f,24.0011005401611f}, {506.555206298828f,51.7324333190918f,27.0011005401611f}, {503.82861328125f,47.7324333190918f,30.7509002685547f}, {507.45361328125f,47.7324333190918f,32.8134994506836f}, {503.82861328125f,47.7324333190918f,33.7509002685547f}, {503.82861328125f,47.7324333190918f,29.2511005401611f}, {503.82861328125f,47.7324333190918f,26.2511005401611f}, {507.45361328125f,47.7324333190918f,27.1885013580322f}, {493.921813964844f,57.2792320251465f,36.0009002685547f}, {491.425201416016f,54.5058326721191f,36.0009002685547f}, {497.195404052734f,57.7058334350586f,24.0011005401611f}, {496.418792724609f,60.052433013916f,24.0011005401611f}, {509.188812255859f,60.052433013916f,36.0009002685547f}, {511.675415039062f,57.2792320251465f,24.0011005401611f}, {514.161987304688f,54.5058326721191f,24.0011005401611f}, {507.45361328125f,47.7324333190918f,34.3134994506836f}, {503.82861328125f,47.7324333190918f,35.2509002685547f}, {507.45361328125f,47.7324333190918f,25.6885013580322f}, {503.82861328125f,47.7324333190918f,24.7511005401611f}, {500.20361328125f,47.7324333190918f,31.6885013580322f}, {500.20361328125f,47.7324333190918f,28.3135013580322f}, {500.20361328125f,47.7324333190918f,30.1885013580322f}, {507.45361328125f,47.7324333190918f,29.8135013580322f}, {507.45361328125f,47.7324333190918f,31.3135013580322f}, {507.45361328125f,47.7324333190918f,30.5635013580322f}, {503.82861328125f,47.7324333190918f,36.0009002685547f}, {507.45361328125f,47.7324333190918f,35.4384994506836f}, {507.45361328125f,47.7324333190918f,35.0634994506836f}, {507.45361328125f,47.7324333190918f,28.6885013580322f}, {507.45361328125f,47.7324333190918f,29.4385013580322f}, {503.82861328125f,47.7324333190918f,24.0011005401611f}, {507.45361328125f,47.7324333190918f,24.5635013580322f}, {507.45361328125f,47.7324333190918f,24.9385013580322f}, {500.20361328125f,47.7324333190918f,34.6884994506836f}, {500.20361328125f,47.7324333190918f,33.1884994506836f}, {500.20361328125f,47.7324333190918f,33.9384994506836f}, {500.20361328125f,47.7324333190918f,25.3135013580322f}, {500.20361328125f,47.7324333190918f,26.8135013580322f}, {500.20361328125f,47.7324333190918f,26.0635013580322f}, {500.20361328125f,47.7324333190918f,30.9385013580322f}, {500.20361328125f,47.7324333190918f,35.0634994506836f}, {500.20361328125f,47.7324333190918f,35.4384994506836f}, {500.20361328125f,47.7324333190918f,29.0635013580322f}, {500.20361328125f,47.7324333190918f,29.4385013580322f}, {500.20361328125f,47.7324333190918f,24.9385013580322f}, {500.20361328125f,47.7324333190918f,24.5635013580322f}, {507.45361328125f,47.7324333190918f,24.1885013580322f}, {507.45361328125f,47.7324333190918f,24.0011005401611f}, {513.86376953125f,49.7324333190918f,24.0011005401611f}, {507.45361328125f,47.7324333190918f,35.8134994506836f}, {507.45361328125f,47.7324333190918f,36.0009002685547f}, {513.86376953125f,49.7324333190918f,36.0009002685547f}, {500.20361328125f,47.7324333190918f,24.1885013580322f}, {500.20361328125f,47.7324333190918f,24.0011005401611f}, {502.988800048828f,49.7324333190918f,24.0011005401611f}, {500.20361328125f,47.7324333190918f,35.8134994506836f}, {500.20361328125f,47.7324333190918f,36.0009002685547f}, {502.988800048828f,49.7324333190918f,36.0009002685547f}, {504.755187988281f,62.8258323669434f,27.0011005401611f}, {499.205383300781f,51.2324333190918f,36.0009002685547f}, {498.786193847656f,51.1074333190918f,36.0009002685547f}, {502.358795166016f,51.2324333190918f,36.0009002685547f}, {499.205383300781f,51.2324333190918f,24.0011005401611f}, {502.358795166016f,51.2324333190918f,24.0011005401611f}, {498.786193847656f,51.1074333190918f,24.0011005401611f}, {502.568786621094f,50.7324333190918f,24.0011005401611f}, {505.931213378906f,51.3574333190918f,24.0011005401611f}, {509.503601074219f,51.4824333190918f,24.0011005401611f}, {502.568786621094f,50.7324333190918f,36.0009002685547f}, {505.931213378906f,51.3574333190918f,36.0009002685547f}, {509.503601074219f,51.4824333190918f,36.0009002685547f}, {499.048583984375f,50.4824333190918f,36.0009002685547f}, {492.428588867188f,48.9824333190918f,36.0009002685547f}, {499.048583984375f,50.4824333190918f,24.0011005401611f}, {492.428588867188f,48.9824333190918f,24.0011005401611f}, {506.088806152344f,50.9824333190918f,24.0011005401611f}, {506.036010742188f,51.1074333190918f,24.0011005401611f}, {506.088806152344f,50.9824333190918f,36.0009002685547f}, {506.036010742188f,51.1074333190918f,36.0009002685547f}, {498.891204833984f,50.8574333190918f,36.0009002685547f}, {498.943786621094f,50.7324333190918f,36.0009002685547f}, {498.891204833984f,50.8574333190918f,24.0011005401611f}, {498.943786621094f,50.7324333190918f,24.0011005401611f}, {499.573608398438f,49.2324333190918f,24.0011005401611f}, {499.783813476562f,48.7324333190918f,24.0011005401611f}, {499.573608398438f,49.2324333190918f,36.0009002685547f}, {499.783813476562f,48.7324333190918f,36.0009002685547f}, {506.403594970703f,50.2324333190918f,24.0011005401611f}, {506.298797607422f,50.4824333190918f,24.0011005401611f}, {506.403594970703f,50.2324333190918f,36.0009002685547f}, {506.298797607422f,50.4824333190918f,36.0009002685547f}, {501.228607177734f,81.599235534668f,27.0011005401611f}, {502.928588867188f,81.599235534668f,24.0011005401611f}, {499.2587890625f,49.9824333190918f,36.0009002685547f}, {499.363800048828f,49.7324333190918f,36.0009002685547f}, {499.2587890625f,49.9824333190918f,24.0011005401611f}, {499.363800048828f,49.7324333190918f,24.0011005401611f}, {496.695404052734f,56.2124328613281f,27.0011005401611f}, {496.195404052734f,54.7190322875977f,24.0011005401611f}, {509.851989746094f,53.2258338928223f,27.0011005401611f}, {493.464782714844f,51.1074333190918f,36.0009002685547f}, {493.464782714844f,51.1074333190918f,24.0011005401611f}, {502.768798828125f,51.7324333190918f,24.0011005401611f}, {500.215789794922f,51.3574333190918f,24.0011005401611f}, {497.628601074219f,51.2324333190918f,24.0011005401611f}, {502.768798828125f,51.7324333190918f,36.0009002685547f}, {500.215789794922f,51.3574333190918f,36.0009002685547f}, {497.628601074219f,51.2324333190918f,36.0009002685547f}, {507.033813476562f,48.7324333190918f,24.0011005401611f}, {506.823791503906f,49.2324333190918f,24.0011005401611f}, {507.033813476562f,48.7324333190918f,36.0009002685547f}, {506.823791503906f,49.2324333190918f,36.0009002685547f}, {494.4501953125f,51.1074333190918f,24.0011005401611f}, {494.4501953125f,51.1074333190918f,36.0009002685547f}, {500.807006835938f,51.3574333190918f,36.0009002685547f}, {503.591186523438f,51.4824333190918f,36.0009002685547f}, {503.591186523438f,51.4824333190918f,24.0011005401611f}, {500.807006835938f,51.3574333190918f,24.0011005401611f}, {505.728607177734f,65.7524337768555f,36.0009002685547f}, {505.728607177734f,65.7524337768555f,33.0009002685547f}, {499.221801757812f,72.2124328613281f,36.0009002685547f}, {501.835388183594f,71.6058349609375f,36.0009002685547f}, {506.515197753906f,72.2124328613281f,24.0011005401611f}, {503.781982421875f,71.6058349609375f,24.0011005401611f}, {503.781982421875f,71.6058349609375f,27.0011005401611f}, {499.888793945312f,65.7524337768555f,24.0011005401611f}, {495.695404052734f,53.2258338928223f,33.0009002685547f}, {516.648803710938f,51.7324333190918f,30.0011005401611f}, {498.20361328125f,77.8658294677734f,28.5011005401611f}, {505.585388183594f,57.7058334350586f,33.0009002685547f}, {508.871795654297f,56.2124328613281f,33.0009002685547f}, {499.992004394531f,57.7058334350586f,27.0011005401611f}, {504.628601074219f,81.599235534668f,33.0009002685547f}, {500.861999511719f,62.8258323669434f,33.0009002685547f}, {496.878601074219f,74.1324310302734f,27.0011005401611f}, {496.878601074219f,74.1324310302734f,33.0009002685547f}, {491.57861328125f,59.199031829834f,27.0011005401611f}, {490.253601074219f,55.4658317565918f,28.5011005401611f}, {491.57861328125f,59.199031829834f,33.0009002685547f}, {514.068786621094f,59.199031829834f,27.0011005401611f}, {514.068786621094f,59.199031829834f,33.0009002685547f}, {508.908813476562f,74.1324310302734f,27.0011005401611f}, {507.618804931641f,77.8658294677734f,28.5011005401611f}, {508.908813476562f,74.1324310302734f,33.0009002685547f}, {491.271789550781f,50.9824333190918f,36.0009002685547f}, {490.877807617188f,50.9824333190918f,36.0009002685547f}, {491.271789550781f,50.9824333190918f,24.0011005401611f}, {490.877807617188f,50.9824333190918f,24.0011005401611f}, {495.213806152344f,50.9824333190918f,36.0009002685547f}, {493.636993408203f,50.9824333190918f,36.0009002685547f}, {495.213806152344f,50.9824333190918f,24.0011005401611f}, {493.636993408203f,50.9824333190918f,24.0011005401611f}, {503.985412597656f,51.4824333190918f,36.0009002685547f}, {503.985412597656f,51.4824333190918f,24.0011005401611f}, {511.675415039062f,57.2792320251465f,36.0009002685547f}, {493.921813964844f,57.2792320251465f,24.0011005401611f}, {502.768798828125f,51.7324333190918f,30.0011005401611f}, {506.555206298828f,51.7324333190918f,30.0011005401611f}, {498.981994628906f,51.7324333190918f,30.0011005401611f}, {492.848815917969f,50.9824333190918f,24.0011005401611f}, {492.848815917969f,50.9824333190918f,36.0009002685547f}, {500.861999511719f,68.6792297363281f,36.0009002685547f}, {500.861999511719f,68.6792297363281f,24.0011005401611f}, {496.878601074219f,74.1324310302734f,24.0011005401611f}, {496.878601074219f,74.1324310302734f,36.0009002685547f}, {504.755187988281f,68.6792297363281f,24.0011005401611f}, {504.755187988281f,68.6792297363281f,36.0009002685547f}, {508.908813476562f,74.1324310302734f,36.0009002685547f}, {508.908813476562f,74.1324310302734f,24.0011005401611f}, {505.728607177734f,65.7524337768555f,30.0011005401611f}, {504.755187988281f,68.6792297363281f,30.0011005401611f}, {503.781982421875f,71.6058349609375f,30.0011005401611f}, {500.861999511719f,68.6792297363281f,30.0011005401611f}, {499.888793945312f,65.7524337768555f,30.0011005401611f}, {501.835388183594f,71.6058349609375f,30.0011005401611f}, {491.57861328125f,59.199031829834f,24.0011005401611f}, {491.57861328125f,59.199031829834f,36.0009002685547f}, {514.068786621094f,59.199031829834f,36.0009002685547f}, {514.068786621094f,59.199031829834f,24.0011005401611f}, {511.07861328125f,47.7324333190918f,34.8759002685547f}, {511.07861328125f,47.7324333190918f,31.8759002685547f}, {514.70361328125f,47.7324333190918f,33.9384994506836f}, {511.07861328125f,47.7324333190918f,25.1261005401611f}, {514.70361328125f,47.7324333190918f,26.0635013580322f}, {511.07861328125f,47.7324333190918f,28.1261005401611f}, {502.788818359375f,57.7058334350586f,30.0011005401611f}, {502.048583984375f,79.8324356079102f,36.0009002685547f}, {514.70361328125f,47.7324333190918f,30.9385013580322f}, {511.07861328125f,47.7324333190918f,30.3759002685547f}, {514.70361328125f,47.7324333190918f,29.0635013580322f}, {511.07861328125f,47.7324333190918f,29.6261005401611f}, {496.57861328125f,47.7324333190918f,31.1259002685547f}, {496.57861328125f,47.7324333190918f,32.6259002685547f}, {496.57861328125f,47.7324333190918f,34.1259002685547f}, {496.57861328125f,47.7324333190918f,28.8761005401611f}, {496.57861328125f,47.7324333190918f,27.3761005401611f}, {496.57861328125f,47.7324333190918f,25.8761005401611f}, {496.57861328125f,47.7324333190918f,29.6261005401611f}, {514.70361328125f,47.7324333190918f,35.4384994506836f}, {511.07861328125f,47.7324333190918f,35.6259002685547f}, {514.70361328125f,47.7324333190918f,24.5635013580322f}, {511.07861328125f,47.7324333190918f,24.3761005401611f}, {496.57861328125f,47.7324333190918f,34.8759002685547f}, {496.57861328125f,47.7324333190918f,25.1261005401611f}, {496.57861328125f,47.7324333190918f,35.6259002685547f}, {496.57861328125f,47.7324333190918f,24.3761005401611f}, {511.07861328125f,47.7324333190918f,36.0009002685547f}, {511.07861328125f,47.7324333190918f,24.0011005401611f}, {514.70361328125f,47.7324333190918f,30.1885013580322f}, {514.70361328125f,47.7324333190918f,35.8134994506836f}, {514.70361328125f,47.7324333190918f,29.8135013580322f}, {514.70361328125f,47.7324333190918f,24.1885013580322f}, {496.57861328125f,47.7324333190918f,36.0009002685547f}, {496.57861328125f,47.7324333190918f,24.0011005401611f}, {510.238800048828f,49.7324333190918f,24.0011005401611f}, {510.238800048828f,49.7324333190918f,36.0009002685547f}, {514.70361328125f,47.7324333190918f,24.0011005401611f}, {514.70361328125f,47.7324333190918f,36.0009002685547f}, {496.158813476562f,48.7324333190918f,36.0009002685547f}, {496.158813476562f,48.7324333190918f,24.0011005401611f}, {502.808807373047f,62.8258323669434f,30.0011005401611f}, {509.608795166016f,51.2324333190918f,24.0011005401611f}, {509.608795166016f,51.2324333190918f,36.0009002685547f}, {491.641204833984f,50.8574333190918f,24.0011005401611f}, {495.423797607422f,50.4824333190918f,36.0009002685547f}, {495.423797607422f,50.4824333190918f,24.0011005401611f}, {491.641204833984f,50.8574333190918f,36.0009002685547f}, {495.528594970703f,50.2324333190918f,24.0011005401611f}, {492.0087890625f,49.9824333190918f,24.0011005401611f}, {509.818786621094f,50.7324333190918f,24.0011005401611f}, {495.948608398438f,49.2324333190918f,36.0009002685547f}, {495.528594970703f,50.2324333190918f,36.0009002685547f}, {495.948608398438f,49.2324333190918f,24.0011005401611f}, {509.818786621094f,50.7324333190918f,36.0009002685547f}, {492.0087890625f,49.9824333190918f,36.0009002685547f}, {491.956207275391f,50.1074333190918f,24.0011005401611f}, {491.956207275391f,50.1074333190918f,36.0009002685547f}, {502.928588867188f,81.599235534668f,30.0011005401611f}, {491.851013183594f,50.3574333190918f,36.0009002685547f}, {491.851013183594f,50.3574333190918f,24.0011005401611f}, {496.195404052734f,54.7190322875977f,30.0011005401611f}, {509.361999511719f,54.7190322875977f,30.0011005401611f}, {488.632598876953f,51.7256317138672f,30.0011005401611f}, {488.632598876953f,51.7256317138672f,29.5091018676758f}, {488.632598876953f,51.7188339233398f,24.0011005401611f}, {488.632598876953f,51.7256317138672f,27.4929008483887f}, {488.632598876953f,51.7324333190918f,30.0011005401611f}, {488.632598876953f,51.7324333190918f,29.0175018310547f}, {488.632598876953f,51.7324333190918f,24.9847011566162f}, {488.632598876953f,51.7324333190918f,24.0011005401611f}, {488.632598876953f,51.7188339233398f,30.0011005401611f}, {488.632598876953f,51.7176322937012f,24.0011005401611f}, {488.632598876953f,51.7182312011719f,30.0011005401611f}, {488.632598876953f,51.7176322937012f,30.0011005401611f}, {488.632598876953f,51.715030670166f,24.0011005401611f}, {488.632598876953f,51.7162322998047f,30.0011005401611f}, {488.632598876953f,50.761833190918f,24.0011005401611f}, {488.632598876953f,50.7578315734863f,24.0011005401611f}, {488.632598876953f,50.7598342895508f,30.0011005401611f}, {488.632598876953f,50.7522315979004f,24.0011005401611f}, {488.632598876953f,49.7838325500488f,24.0011005401611f}, {488.632598876953f,50.2680320739746f,30.0011005401611f}, {488.632598876953f,51.7046318054199f,24.0011005401611f}, {488.632598876953f,51.709831237793f,30.0011005401611f}, {488.632598876953f,50.9120330810547f,24.0011005401611f}, {488.632598876953f,50.8882331848145f,24.0011005401611f}, {488.632598876953f,50.9002304077148f,30.0011005401611f}, {488.632598876953f,47.7324333190918f,24.0370998382568f}, {488.632598876953f,48.5612335205078f,30.0011005401611f}, {488.632598876953f,47.7324333190918f,24.0011005401611f}, {488.632598876953f,47.7324333190918f,24.1091003417969f}, {488.632598876953f,48.5612335205078f,30.0189018249512f}, {488.632598876953f,47.7324333190918f,25.3211002349854f}, {488.632598876953f,48.5612335205078f,30.0551013946533f}, {488.632598876953f,47.7324333190918f,25.4651012420654f}, {488.632598876953f,48.5612335205078f,30.6609001159668f}, {488.632598876953f,47.7324333190918f,25.5371017456055f}, {488.632598876953f,48.5612335205078f,30.7329006195068f}, {488.632598876953f,47.7324333190918f,25.6091003417969f}, {488.632598876953f,48.5612335205078f,30.7689018249512f}, {488.632598876953f,47.7324333190918f,25.8971004486084f}, {488.632598876953f,48.5612335205078f,30.8051013946533f}, {488.632598876953f,47.7324333190918f,28.321102142334f}, {488.632598876953f,48.5612335205078f,30.9491004943848f}, {488.632598876953f,47.7324333190918f,28.4651012420654f}, {488.632598876953f,48.5612335205078f,32.1609001159668f}, {488.632598876953f,47.7324333190918f,28.5371017456055f}, {488.632598876953f,48.5612335205078f,32.2329025268555f}, {488.632598876953f,47.7324333190918f,28.6811008453369f}, {488.632598876953f,48.5612335205078f,32.2689018249512f}, {488.632598876953f,47.7324333190918f,31.1049003601074f}, {488.632598876953f,48.5612335205078f,32.3411026000977f}, {488.632598876953f,47.7324333190918f,31.3929004669189f}, {488.632598876953f,49.3900299072266f,36.0009002685547f}, {488.632598876953f,47.7324333190918f,31.536901473999f}, {488.632598876953f,47.7324333190918f,31.6809005737305f}, {488.632598876953f,47.7324333190918f,34.1049003601074f}, {488.632598876953f,47.7324333190918f,34.3929023742676f}, {488.632598876953f,47.7324333190918f,34.464900970459f}, {488.632598876953f,47.7324333190918f,34.5369033813477f}, {488.632598876953f,47.7324333190918f,34.6809005737305f}, {488.632598876953f,47.7324333190918f,35.8929023742676f}, {488.632598876953f,47.7324333190918f,35.964900970459f}, {488.632598876953f,47.7324333190918f,36.0009002685547f}, {488.632598876953f,50.8816299438477f,24.0011005401611f}, {488.632598876953f,50.8850326538086f,30.0011005401611f}, {488.632598876953f,49.7480316162109f,24.0011005401611f}, {488.632598876953f,49.7426300048828f,24.0011005401611f}, {488.632598876953f,49.745231628418f,30.0011005401611f}, {488.632598876953f,49.7592315673828f,24.0011005401611f}, {488.632598876953f,49.7536315917969f,30.0011005401611f}, {488.632598876953f,49.3900299072266f,24.0011005401611f}, {488.632598876953f,49.5664329528809f,30.0011005401611f}, {488.632598876953f,50.8786315917969f,24.0011005401611f}, {488.632598876953f,50.7764320373535f,24.0011005401611f}, {488.632598876953f,50.8274307250977f,30.0011005401611f}, {488.632598876953f,50.7550315856934f,30.0011005401611f}, {488.632598876953f,50.7692337036133f,30.0011005401611f}, {488.632598876953f,50.9284324645996f,24.0011005401611f}, {488.632598876953f,50.9202308654785f,30.0011005401611f}, {488.632598876953f,51.1788330078125f,24.0011005401611f}, {488.632598876953f,51.139232635498f,24.0011005401611f}, {488.632598876953f,51.1590309143066f,30.0011005401611f}, {488.632598876953f,51.2324333190918f,24.0011005401611f}, {488.632598876953f,51.2056312561035f,30.0011005401611f}, {488.632598876953f,51.4340324401855f,24.0011005401611f}, {488.632598876953f,51.3946304321289f,24.0011005401611f}, {488.632598876953f,51.4142303466797f,30.0011005401611f}, {488.632598876953f,51.4498329162598f,24.0011005401611f}, {488.632598876953f,51.5772323608398f,30.0011005401611f}, {488.632598876953f,51.4418334960938f,30.0011005401611f}, {488.632598876953f,51.3136329650879f,30.0011005401611f}, {488.632598876953f,49.7714309692383f,30.0011005401611f}, {488.632598876953f,51.0338325500488f,30.0011005401611f}, {488.632598876953f,50.8816299438477f,30.0011005401611f}, {488.632598876953f,50.8800315856934f,30.0011005401611f}, {488.632598876953f,51.7188339233398f,36.0009002685547f}, {488.632598876953f,51.7176322937012f,36.0009002685547f}, {488.632598876953f,49.3900299072266f,30.0011005401611f}, {488.632598876953f,50.7522315979004f,30.0011005401611f}, {488.632598876953f,50.7522315979004f,36.0009002685547f}, {488.632598876953f,49.7426300048828f,30.0011005401611f}, {488.632598876953f,49.7426300048828f,36.0009002685547f}, {488.632598876953f,49.7480316162109f,30.0011005401611f}, {488.632598876953f,49.7480316162109f,36.0009002685547f}, {488.632598876953f,51.715030670166f,30.0011005401611f}, {488.632598876953f,51.715030670166f,36.0009002685547f}, {488.632598876953f,50.7578315734863f,30.0011005401611f}, {488.632598876953f,50.7578315734863f,36.0009002685547f}, {488.632598876953f,50.761833190918f,30.0011005401611f}, {488.632598876953f,50.761833190918f,36.0009002685547f}, {488.632598876953f,50.8882331848145f,30.0011005401611f}, {488.632598876953f,50.8882331848145f,36.0009002685547f}, {488.632598876953f,49.7592315673828f,30.0011005401611f}, {488.632598876953f,49.7592315673828f,36.0009002685547f}, {488.632598876953f,51.1788330078125f,30.0011005401611f}, {488.632598876953f,51.1788330078125f,36.0009002685547f}, {488.632598876953f,50.9120330810547f,30.0011005401611f}, {488.632598876953f,50.9120330810547f,36.0009002685547f}, {488.632598876953f,51.4498329162598f,30.0011005401611f}, {488.632598876953f,51.4498329162598f,36.0009002685547f}, {488.632598876953f,51.7046318054199f,30.0011005401611f}, {488.632598876953f,51.7046318054199f,36.0009002685547f}, {488.632598876953f,51.2324333190918f,30.0011005401611f}, {488.632598876953f,51.2324333190918f,36.0009002685547f}, {488.632598876953f,51.3946304321289f,30.0011005401611f}, {488.632598876953f,51.3946304321289f,36.0009002685547f}, {488.632598876953f,51.4340324401855f,30.0011005401611f}, {488.632598876953f,51.4340324401855f,36.0009002685547f}, {488.632598876953f,49.7838325500488f,30.0011005401611f}, {488.632598876953f,49.7838325500488f,36.0009002685547f}, {488.632598876953f,50.7764320373535f,30.0011005401611f}, {488.632598876953f,50.7764320373535f,36.0009002685547f}, {488.632598876953f,51.139232635498f,30.0011005401611f}, {488.632598876953f,51.139232635498f,36.0009002685547f}, {488.632598876953f,50.9284324645996f,30.0011005401611f}, {488.632598876953f,50.9284324645996f,36.0009002685547f}, {488.632598876953f,50.8816299438477f,36.0009002685547f}, {488.632598876953f,50.8786315917969f,30.0011005401611f}, {488.632598876953f,50.8786315917969f,36.0009002685547f}, {488.632598876953f,51.7324333190918f,35.0173034667969f}, {488.632598876953f,51.7324333190918f,36.0009002685547f}, {488.632598876953f,51.7324333190918f,30.9847011566162f}, {517.188415527344f,51.7140884399414f,24.0011005401611f}, {517.188415527344f,51.7140884399414f,36.0009002685547f}, {517.188415527344f,50.4475173950195f,24.0011005401611f}, {517.188415527344f,51.7324333190918f,35.3734130859375f}, {517.188415527344f,51.7324333190918f,36.0009002685547f}, {517.188415527344f,51.7324333190918f,34.1185760498047f}, {517.188415527344f,51.7324333190918f,31.88330078125f}, {517.188415527344f,51.7324333190918f,30.0011005401611f}, {517.188415527344f,51.7324333190918f,28.1187744140625f}, {517.188415527344f,51.7324333190918f,25.8834266662598f}, {517.188415527344f,51.7324333190918f,24.6285915374756f}, {517.188415527344f,51.7324333190918f,24.0011005401611f}, {517.188415527344f,47.7324333190918f,24.0600452423096f}, {517.188415527344f,47.7324333190918f,24.0011005401611f}, {517.188415527344f,50.4475173950195f,36.0009002685547f}, {517.188415527344f,47.7324333190918f,24.1779975891113f}, {517.188415527344f,47.7324333190918f,24.6498031616211f}, {517.188415527344f,47.7324333190918f,28.7625770568848f}, {517.188415527344f,47.7324333190918f,29.7061901092529f}, {517.188415527344f,47.7324333190918f,29.9420928955078f}, {517.188415527344f,47.7324333190918f,30.0600452423096f}, {517.188415527344f,47.7324333190918f,30.2959480285645f}, {517.188415527344f,47.7324333190918f,31.2395629882812f}, {517.188415527344f,47.7324333190918f,35.3521995544434f}, {517.188415527344f,47.7324333190918f,35.8240051269531f}, {517.188415527344f,47.7324333190918f,35.9419555664062f}, {517.188415527344f,47.7324333190918f,36.0009002685547f} }, + { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}, {12,2,1}, {12,1,13}, {14,15,16}, {17,18,19}, {20,21,22}, {17,19,23}, {24,25,26}, {27,13,1}, {28,25,29}, {30,31,32}, {28,33,34}, {35,36,7}, {37,38,39}, {40,10,41}, {42,43,44}, {45,5,4}, {46,47,48}, {46,48,49}, {45,4,50}, {51,52,53}, {51,54,55}, {56,52,57}, {58,59,60}, {61,50,4}, {62,63,64}, {65,34,33}, {66,67,42}, {68,17,69}, {70,71,22}, {66,42,72}, {73,16,15}, {35,7,74}, {75,76,54}, {77,27,1}, {78,32,31}, {75,54,79}, {80,26,25}, {81,80,25}, {82,83,48}, {84,20,85}, {81,25,86}, {87,88,19}, {0,89,1}, {90,91,92}, {90,10,93}, {38,94,39}, {94,95,39}, {3,7,96}, {97,15,98}, {97,99,15}, {92,91,100}, {89,101,1}, {102,39,95}, {103,11,10}, {104,96,7}, {105,15,99}, {106,61,4}, {107,108,33}, {76,55,54}, {109,91,110}, {111,23,19}, {112,63,113}, {114,115,48}, {116,59,117}, {118,20,119}, {120,31,121}, {122,44,43}, {110,91,123}, {124,125,126}, {127,128,129}, {127,130,124}, {131,124,132}, {126,133,134}, {135,136,126}, {137,138,127}, {139,127,138}, {128,140,141}, {142,128,143}, {144,140,145}, {100,91,146}, {147,148,134}, {101,149,1}, {102,150,39}, {103,10,151}, {145,140,152}, {152,140,153}, {148,154,134}, {154,155,134}, {156,15,105}, {157,104,7}, {36,8,7}, {158,37,39}, {159,19,88}, {160,19,159}, {161,59,58}, {161,117,59}, {162,31,30}, {162,121,31}, {163,43,164}, {163,165,43}, {166,167,43}, {167,164,43}, {168,57,52}, {82,48,169}, {114,170,171}, {108,65,33}, {64,63,112}, {114,172,170}, {160,173,170}, {171,170,173}, {172,174,170}, {160,170,174}, {175,176,177}, {178,77,1}, {179,31,120}, {175,180,176}, {181,182,176}, {177,176,182}, {180,183,176}, {181,176,183}, {184,42,67}, {185,69,17}, {160,111,19}, {186,187,160}, {188,189,114}, {190,188,114}, {114,48,191}, {192,114,193}, {194,160,195}, {196,160,194}, {197,198,181}, {199,197,181}, {122,43,165}, {200,201,175}, {202,175,203}, {204,175,202}, {205,119,20}, {206,181,207}, {208,209,15}, {210,15,209}, {211,10,9}, {212,10,211}, {213,214,215}, {216,217,218}, {219,14,17}, {113,63,220}, {221,222,48}, {191,48,222}, {22,223,20}, {205,20,223}, {224,40,42}, {123,91,225}, {214,226,215}, {227,215,226}, {218,217,228}, {229,228,217}, {215,230,213}, {125,135,126}, {217,216,231}, {129,128,142}, {216,213,232}, {130,132,124}, {213,216,233}, {234,213,235}, {236,227,237}, {238,237,227}, {239,240,216}, {233,216,240}, {241,242,229}, {243,229,242}, {215,227,244}, {245,215,246}, {217,247,229}, {248,249,217}, {232,213,250}, {230,250,213}, {133,147,134}, {244,227,251}, {236,252,227}, {251,227,252}, {231,216,253}, {254,253,216}, {141,140,144}, {247,255,229}, {241,229,256}, {255,256,229}, {257,241,258}, {259,146,91}, {260,261,236}, {262,1,149}, {263,264,241}, {265,241,264}, {266,236,267}, {268,267,236}, {49,48,83}, {166,43,269}, {270,271,272}, {273,274,275}, {276,274,277}, {278,151,10}, {279,280,272}, {281,39,150}, {272,282,279}, {155,283,134}, {274,276,284}, {153,140,285}, {286,276,287}, {265,276,286}, {288,289,279}, {268,288,279}, {290,291,272}, {271,290,272}, {292,274,293}, {275,274,292}, {294,265,295}, {276,265,294}, {296,297,268}, {279,296,268}, {241,265,298}, {298,265,299}, {236,300,268}, {300,301,268}, {107,33,78}, {302,303,59}, {304,305,279}, {282,304,279}, {306,276,307}, {284,276,306}, {185,17,73}, {308,309,221}, {158,39,70}, {310,41,10}, {15,311,208}, {7,6,312}, {313,314,6}, {315,6,314}, {316,208,317}, {318,317,208}, {258,241,319}, {319,241,320}, {261,321,236}, {321,322,236}, {6,315,323}, {208,324,318}, {270,325,318}, {326,318,325}, {327,328,315}, {273,315,328}, {118,329,20}, {330,20,329}, {331,332,25}, {86,25,332}, {333,334,52}, {335,52,334}, {115,336,48}, {169,48,336}, {62,106,4}, {35,15,210}, {35,337,15}, {158,10,212}, {158,310,10}, {338,178,1}, {339,59,116}, {107,302,59}, {66,22,340}, {66,341,22}, {185,221,342}, {185,308,221}, {75,31,179}, {75,343,31}, {166,20,330}, {166,85,20}, {81,52,335}, {81,168,52}, {82,19,344}, {82,87,19}, {108,339,345}, {346,108,345}, {64,347,348}, {349,347,64}, {178,109,350}, {351,178,350}, {179,352,353}, {354,352,179}, {355,208,356}, {356,208,311}, {357,358,6}, {358,312,6}, {68,22,21}, {68,340,22}, {221,48,47}, {184,342,221}, {359,270,360}, {318,360,270}, {361,362,273}, {315,273,362}, {272,102,270}, {363,270,102}, {274,273,103}, {364,103,273}, {21,19,18}, {21,20,84}, {184,46,42}, {43,42,46}, {12,22,71}, {365,22,12}, {14,98,15}, {14,220,63}, {40,93,10}, {40,225,91}, {45,221,309}, {366,221,45}, {313,367,212}, {212,367,368}, {36,369,367}, {313,36,367}, {316,37,367}, {37,368,367}, {210,367,369}, {316,367,210}, {362,370,315}, {370,323,315}, {360,318,371}, {371,318,324}, {372,331,159}, {159,195,160}, {373,115,56}, {115,114,189}, {52,56,161}, {374,161,56}, {25,28,331}, {375,331,28}, {376,333,163}, {163,203,175}, {377,118,24}, {118,181,198}, {25,24,162}, {378,162,24}, {52,51,333}, {379,333,51}, {167,380,381}, {376,167,381}, {377,381,330}, {330,381,380}, {335,381,382}, {376,381,335}, {373,383,169}, {169,383,384}, {168,385,383}, {373,168,383}, {372,87,383}, {87,384,383}, {377,80,381}, {80,382,381}, {86,383,385}, {372,383,86}, {106,348,347}, {386,106,347}, {375,65,346}, {108,346,65}, {64,112,349}, {387,349,112}, {171,190,114}, {346,345,171}, {374,190,345}, {171,345,190}, {349,172,347}, {172,114,192}, {386,347,192}, {172,192,347}, {173,160,196}, {171,173,346}, {375,346,196}, {173,196,346}, {172,349,174}, {174,186,160}, {387,186,349}, {174,349,186}, {64,348,62}, {106,62,348}, {108,107,339}, {59,339,107}, {374,345,116}, {339,116,345}, {76,353,352}, {379,76,352}, {388,77,351}, {178,351,77}, {179,120,354}, {378,354,120}, {177,200,175}, {351,350,177}, {389,200,350}, {177,350,200}, {354,180,352}, {180,175,204}, {379,352,204}, {180,204,352}, {182,181,206}, {177,182,351}, {388,351,206}, {182,206,351}, {180,354,183}, {183,199,181}, {378,199,354}, {183,354,199}, {91,109,338}, {178,338,109}, {76,75,353}, {179,353,75}, {389,350,110}, {109,110,350}, {390,391,392}, {393,394,395}, {224,122,389}, {122,175,201}, {365,388,205}, {205,207,181}, {66,340,396}, {68,396,340}, {184,396,342}, {185,342,396}, {66,396,67}, {184,67,396}, {68,69,396}, {185,396,69}, {219,111,387}, {111,160,187}, {366,386,191}, {191,193,114}, {150,272,280}, {102,272,150}, {151,277,274}, {103,151,274}, {161,374,117}, {116,117,374}, {366,61,386}, {106,386,61}, {111,187,387}, {186,387,187}, {56,188,374}, {190,374,188}, {191,386,193}, {192,193,386}, {331,375,194}, {196,194,375}, {28,34,375}, {65,375,34}, {219,387,113}, {112,113,387}, {224,389,123}, {110,123,389}, {51,55,379}, {76,379,55}, {24,197,378}, {199,378,197}, {122,201,389}, {200,389,201}, {333,379,202}, {204,202,379}, {205,388,207}, {206,207,388}, {365,27,388}, {77,388,27}, {162,378,121}, {120,121,378}, {162,30,25}, {30,29,25}, {51,53,54}, {303,60,59}, {28,29,33}, {29,397,33}, {161,58,52}, {53,52,58}, {21,84,19}, {84,344,19}, {46,49,43}, {49,269,43}, {208,316,209}, {210,209,316}, {327,313,211}, {212,211,313}, {36,35,369}, {210,369,35}, {37,158,368}, {212,368,158}, {6,8,313}, {36,313,8}, {326,38,316}, {37,316,38}, {392,391,398}, {399,398,391}, {394,400,395}, {401,395,400}, {390,214,391}, {214,213,234}, {393,395,218}, {218,239,216}, {402,230,403}, {230,215,245}, {125,124,131}, {404,125,403}, {405,406,231}, {231,248,217}, {129,137,127}, {407,406,129}, {130,127,139}, {402,130,408}, {194,195,331}, {159,331,195}, {115,189,56}, {188,56,189}, {14,219,220}, {113,220,219}, {45,50,366}, {61,366,50}, {221,366,222}, {191,222,366}, {17,23,219}, {111,219,23}, {118,198,24}, {197,24,198}, {202,203,333}, {163,333,203}, {40,224,225}, {123,225,224}, {12,13,365}, {27,365,13}, {22,365,223}, {205,223,365}, {42,44,224}, {122,224,44}, {399,391,234}, {214,234,391}, {401,239,395}, {218,395,239}, {214,390,226}, {226,238,227}, {218,228,393}, {228,229,243}, {401,399,233}, {233,235,213}, {392,409,390}, {410,390,409}, {394,393,411}, {412,411,393}, {402,403,131}, {125,131,403}, {405,137,406}, {129,406,137}, {405,408,139}, {130,139,408}, {230,245,403}, {404,403,245}, {231,406,248}, {407,248,406}, {232,254,216}, {402,408,232}, {413,404,244}, {244,246,215}, {414,247,407}, {247,217,249}, {133,126,136}, {415,133,413}, {141,143,128}, {416,414,141}, {410,238,390}, {226,390,238}, {412,393,243}, {228,243,393}, {233,399,235}, {234,235,399}, {237,260,236}, {238,410,237}, {417,260,410}, {237,410,260}, {239,401,240}, {233,240,401}, {242,241,257}, {243,242,412}, {418,412,257}, {242,257,412}, {401,419,399}, {398,399,419}, {417,410,420}, {409,420,410}, {400,421,401}, {419,401,421}, {418,422,412}, {411,412,422}, {413,135,404}, {125,404,135}, {414,407,142}, {129,142,407}, {130,402,132}, {131,132,402}, {133,136,413}, {135,413,136}, {423,147,415}, {133,415,147}, {137,405,138}, {139,138,405}, {141,414,143}, {142,143,414}, {424,416,144}, {141,144,416}, {405,254,408}, {232,408,254}, {244,404,246}, {245,246,404}, {247,249,407}, {248,407,249}, {232,250,402}, {230,402,250}, {415,413,251}, {244,251,413}, {252,236,266}, {251,252,415}, {423,415,266}, {252,266,415}, {231,253,405}, {254,405,253}, {416,255,414}, {247,414,255}, {256,263,241}, {255,416,256}, {424,263,416}, {256,416,263}, {257,258,418}, {425,418,258}, {260,417,261}, {426,261,417}, {422,418,427}, {427,259,91}, {420,428,417}, {428,1,262}, {147,423,148}, {429,148,423}, {263,424,264}, {264,295,265}, {266,267,423}, {267,268,297}, {144,145,424}, {430,424,145}, {49,431,269}, {166,269,431}, {82,431,83}, {49,83,431}, {84,85,431}, {166,431,85}, {82,344,431}, {84,431,344}, {432,278,90}, {10,90,278}, {433,0,281}, {39,281,0}, {362,361,434}, {435,271,359}, {270,359,271}, {436,361,275}, {273,275,361}, {360,437,359}, {277,287,276}, {151,278,277}, {280,279,289}, {150,280,281}, {436,438,439}, {439,285,140}, {90,92,432}, {440,432,92}, {282,272,291}, {441,282,442}, {284,293,274}, {443,438,284}, {278,432,286}, {286,299,265}, {281,288,433}, {288,268,301}, {0,433,89}, {444,89,433}, {435,445,442}, {445,134,283}, {439,446,436}, {361,436,446}, {442,290,435}, {271,435,290}, {438,436,292}, {275,292,436}, {445,435,447}, {359,447,435}, {286,287,278}, {277,278,287}, {288,281,289}, {280,289,281}, {145,152,430}, {443,430,152}, {148,429,154}, {441,154,429}, {424,430,294}, {294,307,276}, {423,296,429}, {296,279,305}, {425,440,100}, {92,100,440}, {290,442,291}, {282,291,442}, {292,293,438}, {284,438,293}, {298,320,241}, {432,440,298}, {300,236,322}, {433,300,444}, {426,101,444}, {89,444,101}, {107,448,302}, {302,79,54}, {78,31,343}, {107,78,448}, {75,79,448}, {302,448,79}, {78,343,448}, {75,448,343}, {427,418,259}, {425,259,418}, {428,262,417}, {426,417,262}, {437,449,359}, {447,359,449}, {434,361,450}, {446,450,361}, {32,33,397}, {78,33,32}, {53,303,54}, {302,54,303}, {152,153,443}, {438,443,153}, {429,304,441}, {282,441,304}, {430,443,306}, {284,306,443}, {154,441,155}, {442,155,441}, {298,299,432}, {286,432,299}, {300,433,301}, {288,301,433}, {185,451,308}, {308,74,7}, {73,15,337}, {185,73,451}, {35,74,451}, {308,451,74}, {73,337,451}, {35,451,337}, {158,452,310}, {310,72,42}, {70,22,341}, {158,70,452}, {66,72,452}, {310,452,72}, {70,341,452}, {66,452,341}, {313,327,314}, {315,314,327}, {316,317,326}, {318,326,317}, {15,156,311}, {356,311,156}, {7,312,157}, {358,157,312}, {211,9,327}, {364,327,9}, {38,326,94}, {363,94,326}, {294,295,424}, {264,424,295}, {296,423,297}, {267,297,423}, {262,149,426}, {101,426,149}, {258,319,425}, {440,425,319}, {261,426,321}, {444,321,426}, {259,425,146}, {100,146,425}, {306,307,430}, {294,430,307}, {304,429,305}, {296,305,429}, {319,320,440}, {298,440,320}, {321,444,322}, {300,322,444}, {445,283,442}, {155,442,283}, {439,438,285}, {153,285,438}, {17,68,18}, {21,18,68}, {46,184,47}, {221,47,184}, {102,95,363}, {94,363,95}, {9,11,364}, {103,364,11}, {6,323,357}, {370,357,323}, {371,324,355}, {208,355,324}, {270,363,325}, {326,325,363}, {327,364,328}, {273,328,364}, {0,2,39}, {12,39,2}, {90,93,91}, {40,91,93}, {14,16,17}, {73,17,16}, {45,309,7}, {308,7,309}, {12,71,39}, {70,39,71}, {40,41,42}, {310,42,41}, {97,98,63}, {14,63,98}, {3,5,7}, {45,7,5}, {118,377,329}, {330,329,377}, {331,372,332}, {86,332,372}, {333,376,334}, {335,334,376}, {115,373,336}, {169,336,373}, {167,166,380}, {330,380,166}, {80,81,382}, {335,382,81}, {86,385,81}, {168,81,385}, {169,384,82}, {87,82,384}, {159,88,372}, {87,372,88}, {163,164,376}, {167,376,164}, {24,26,377}, {80,377,26}, {56,57,373}, {168,373,57}, {32,397,30}, {29,30,397}, {58,60,53}, {303,53,60}, {205,181,119}, {118,119,181}, {163,175,165}, {122,165,175}, {453,454,455}, {454,456,455}, {457,455,456}, {458,455,457}, {459,455,458}, {460,455,459}, {461,462,463}, {464,465,466}, {467,468,469}, {470,471,472}, {465,473,474}, {475,476,477}, {478,479,480}, {481,482,478}, {483,484,481}, {485,486,483}, {487,488,485}, {489,490,487}, {491,492,489}, {493,494,491}, {495,496,493}, {497,498,495}, {499,500,497}, {501,502,499}, {503,504,501}, {505,504,503}, {506,504,505}, {507,504,506}, {508,504,507}, {509,504,508}, {510,504,509}, {511,504,510}, {512,504,511}, {513,504,512}, {514,504,513}, {476,515,516}, {517,518,519}, {520,517,521}, {518,522,523}, {522,480,479}, {524,525,526}, {468,470,527}, {525,467,528}, {529,475,530}, {531,532,533}, {534,531,535}, {536,537,538}, {473,539,540}, {539,536,541}, {537,534,542}, {471,520,543}, {532,529,544}, {545,524,546}, {453,461,547}, {463,464,548}, {523,549,504}, {527,550,551}, {519,552,553}, {521,554,555}, {466,556,557}, {469,558,559}, {528,560,561}, {477,562,563}, {543,564,565}, {535,566,567}, {530,568,569}, {540,570,571}, {474,572,573}, {542,574,575}, {538,576,577}, {541,578,579}, {472,580,581}, {526,582,583}, {533,584,585}, {544,586,587}, {516,545,588}, {588,589,590}, {455,460,4}, {591,592,63}, {462,455,4}, {592,547,63}, {547,548,63}, {465,462,4}, {548,557,63}, {127,124,501}, {127,501,499}, {505,503,124}, {124,126,507}, {124,507,506}, {509,508,126}, {126,134,512}, {126,512,511}, {510,509,126}, {128,127,493}, {128,493,491}, {497,495,127}, {489,487,128}, {140,128,483}, {140,483,481}, {487,485,128}, {478,480,140}, {480,522,140}, {514,513,134}, {504,514,134}, {551,581,437}, {471,470,434}, {445,447,555}, {445,555,553}, {134,445,553}, {134,553,504}, {446,439,518}, {446,518,517}, {439,140,522}, {439,522,518}, {515,476,358}, {563,588,356}, {557,573,63}, {473,465,4}, {437,360,559}, {437,559,551}, {360,371,561}, {360,561,559}, {362,434,470}, {362,470,468}, {370,362,468}, {370,468,467}, {499,497,127}, {506,505,124}, {495,493,127}, {513,512,134}, {481,478,140}, {447,449,565}, {447,565,555}, {450,446,517}, {450,517,520}, {356,156,569}, {356,569,563}, {157,358,476}, {157,476,475}, {357,370,467}, {357,467,525}, {371,355,583}, {371,583,561}, {460,459,4}, {63,62,593}, {63,593,591}, {62,4,459}, {62,459,458}, {532,531,104}, {531,534,104}, {567,585,105}, {575,567,105}, {4,3,539}, {4,539,473}, {536,539,3}, {97,63,573}, {97,573,571}, {571,579,97}, {99,97,579}, {99,579,577}, {105,99,577}, {105,577,575}, {96,104,534}, {96,534,537}, {3,96,537}, {3,537,536}, {503,501,124}, {508,507,126}, {491,489,128}, {511,510,126}, {485,483,128}, {434,450,520}, {434,520,471}, {449,437,581}, {449,581,565}, {156,105,585}, {156,585,587}, {587,569,156}, {104,157,529}, {104,529,532}, {475,529,157}, {590,583,355}, {355,356,588}, {355,588,590}, {358,357,524}, {358,524,515}, {525,524,357}, {458,457,62}, {457,593,62}, {479,478,482}, {479,504,549}, {479,482,504}, {482,481,484}, {472,551,550}, {581,551,472}, {482,484,504}, {484,483,486}, {523,553,552}, {504,553,523}, {540,573,572}, {571,573,540}, {544,585,584}, {587,585,544}, {542,577,576}, {575,577,542}, {526,590,589}, {583,590,526}, {535,575,574}, {567,575,535}, {533,567,566}, {585,567,533}, {538,579,578}, {577,579,538}, {543,581,580}, {565,581,543}, {477,569,568}, {563,569,477}, {530,587,586}, {569,587,530}, {541,571,570}, {579,571,541}, {528,583,582}, {561,583,528}, {591,453,592}, {547,592,453}, {521,565,564}, {555,565,521}, {474,557,556}, {573,557,474}, {516,563,562}, {588,563,516}, {519,555,554}, {553,555,519}, {527,559,558}, {551,559,527}, {469,561,560}, {559,561,469}, {462,461,455}, {453,455,461}, {461,463,547}, {548,547,463}, {465,464,462}, {463,462,464}, {464,466,548}, {557,548,466}, {469,560,467}, {528,467,560}, {472,550,470}, {527,470,550}, {474,556,465}, {466,465,556}, {477,568,475}, {530,475,568}, {516,562,476}, {477,476,562}, {519,554,517}, {521,517,554}, {521,564,520}, {543,520,564}, {523,552,518}, {519,518,552}, {479,549,522}, {523,522,549}, {526,589,524}, {589,546,524}, {527,558,468}, {469,468,558}, {528,582,525}, {526,525,582}, {530,586,529}, {544,529,586}, {533,566,531}, {535,531,566}, {535,574,534}, {542,534,574}, {538,578,536}, {541,536,578}, {540,572,473}, {474,473,572}, {541,570,539}, {540,539,570}, {542,576,537}, {538,537,576}, {543,580,471}, {472,471,580}, {544,584,532}, {533,532,584}, {524,545,515}, {516,515,545}, {545,546,588}, {589,588,546}, {453,591,454}, {593,454,591}, {484,486,504}, {486,485,488}, {486,488,504}, {488,487,490}, {488,490,504}, {490,489,492}, {490,492,504}, {492,491,494}, {492,494,504}, {494,493,496}, {494,496,504}, {496,495,498}, {496,498,504}, {498,497,500}, {498,500,504}, {500,499,502}, {500,502,504}, {501,504,502}, {454,593,456}, {457,456,593}, {594,595,596}, {597,598,594}, {599,597,594}, {600,599,594}, {601,600,594}, {602,601,594}, {603,602,594}, {604,603,594}, {605,604,594}, {606,607,608}, {609,606,608}, {610,609,608}, {611,610,608}, {612,611,608}, {613,612,608}, {614,613,608}, {615,614,608}, {616,615,608}, {617,616,608}, {618,617,608}, {619,618,608}, {620,619,608}, {596,608,607}, {595,594,598}, {608,596,595}, {605,594,91}, {91,338,602}, {91,602,603}, {598,597,1}, {594,596,91}, {608,595,1}, {595,598,1}, {616,617,392}, {610,611,394}, {419,421,613}, {419,613,614}, {422,427,607}, {422,607,606}, {427,91,596}, {427,596,607}, {428,420,619}, {428,619,620}, {1,428,620}, {1,620,608}, {420,409,618}, {420,618,619}, {411,422,606}, {411,606,609}, {398,419,614}, {398,614,615}, {421,400,612}, {421,612,613}, {409,392,617}, {409,617,618}, {394,411,609}, {394,609,610}, {604,605,91}, {338,1,599}, {338,599,600}, {392,398,615}, {392,615,616}, {400,394,611}, {400,611,612}, {603,604,91}, {601,602,338}, {597,599,1}, {600,601,338} }); break; case TestMesh::gt2_teeth: mesh = TriangleMesh( - { {15.8899993896484,19.444055557251,2.67489433288574}, {15.9129991531372,19.1590557098389,2.67489433288574}, {15.9039993286133,19.1500549316406,2.67489433288574}, {15.9489994049072,19.2490558624268,2.67489433288574}, - {15.9579992294312,19.3570556640625,2.67489433288574}, {15.8819999694824,18.690055847168,2.67489433288574}, {15.8319997787476,17.7460556030273,2.67489433288574}, {15.8489999771118,18.819055557251,2.67489433288574}, - {15.8589992523193,17.7190551757812,2.67489433288574}, {15.8769998550415,19.0490550994873,2.67489433288574}, {15.7529993057251,17.8080558776855,2.67489433288574}, {15.7869997024536,19.5010547637939,2.67489433288574}, - {14.0329990386963,18.7170543670654,2.67489433288574}, {13.9599990844727,18.7460556030273,2.67489433288574}, {13.9869995117188,20.2840557098389,2.67489433288574}, {14.2029991149902,20.149055480957,2.67489433288574}, - {14.1939992904663,19.9560546875,2.67489433288574}, {14.1939992904663,20.1670551300049,2.67489433288574}, {14.2119998931885,20.0590553283691,2.67489433288574}, {12.1899995803833,19.1840553283691,2.67489433288574}, - {12.096999168396,19.1950550079346,2.67489433288574}, {12.1099996566772,20.6690559387207,2.67489433288574}, {11.382999420166,19.9750556945801,2.67489433288574}, {11.2599992752075,19.2490558624268,2.67489433288574}, - {11.2369995117188,19.9320545196533,2.67489433288574}, {11.5349998474121,20.0640544891357,2.67489433288574}, {11.6259994506836,20.1550559997559,2.67489433288574}, {11.6829986572266,20.2390556335449,2.67489433288574}, - {11.7369995117188,20.3570556640625,2.67489433288574}, {11.8449993133545,20.645055770874,2.67489433288574}, {11.7729988098145,20.4640560150146,2.67489433288574}, {11.7799987792969,20.5370559692383,9.41389465332031}, - {11.7639999389648,20.4470558166504,2.67489433288574}, {11.9559993743896,20.6810550689697,2.67489433288574}, {12.3079996109009,20.6020545959473,2.67489433288574}, {12.1959991455078,19.1860542297363,2.67489433288574}, - {12.2059993743896,20.6540546417236,2.67489433288574}, {12.3489990234375,20.3740558624268,2.67489433288574}, {12.3579998016357,20.2750549316406,2.67489433288574}, {12.3669996261597,20.266056060791,2.67489433288574}, - {12.3849992752075,20.1670551300049,2.67489433288574}, {12.4269990921021,20.0680541992188,2.67489433288574}, {12.5029993057251,19.9540557861328,2.67489433288574}, {12.6169996261597,19.8550548553467,2.67489433288574}, - {12.7449989318848,19.7800559997559,2.67489433288574}, {12.7629995346069,19.7800559997559,2.67489433288574}, {12.8799991607666,19.7350559234619,2.67489433288574}, {13.0369997024536,19.7250556945801,2.67489433288574}, - {13.0149993896484,19.0340557098389,2.67489433288574}, {11.1699991226196,19.2580547332764,2.67489433288574}, {11.0959987640381,19.2580547332764,2.67489433288574}, {11.1209993362427,19.9230556488037,2.67489433288574}, - {13.0599994659424,19.024055480957,2.67489433288574}, {14.9049997329712,18.3170547485352,2.67489433288574}, {14.8779993057251,18.3400554656982,2.67489433288574}, {14.8779993057251,19.149055480957,2.67489433288574}, - {13.3039989471436,19.77805519104,2.67489433288574}, {13.1589994430542,18.9890556335449,2.67489433288574}, {13.1559991836548,19.7350559234619,2.67489433288574}, {13.4269990921021,19.8600559234619,2.67489433288574}, - {13.5339994430542,19.9700546264648,2.67389440536499}, {13.6359996795654,20.1220550537109,2.67489433288574}, {13.6359996795654,20.1400547027588,2.67489433288574}, {13.6719989776611,20.2210559844971,2.67489433288574}, - {13.6899995803833,20.2300548553467,2.67489433288574}, {13.7509994506836,20.3010559082031,2.67489433288574}, {13.8539991378784,20.3180541992188,2.67489433288574}, {14.8329992294312,18.3580551147461,2.67489433288574}, - {14.1849994659424,19.8530559539795,2.67489433288574}, {14.0769996643066,18.7000541687012,2.67489433288574}, {14.1099996566772,20.2400550842285,2.67489433288574}, {14.2009992599487,19.6230545043945,2.67489433288574}, - {14.2729997634888,19.4670543670654,2.67489433288574}, {14.3379993438721,19.3790550231934,2.67489433288574}, {14.4549999237061,19.2770557403564,2.67489433288574}, {14.5899991989136,19.2040557861328,2.67489433288574}, - {14.6079998016357,19.2040557861328,2.67489433288574}, {14.7209997177124,19.1600551605225,2.67489433288574}, {15.1379995346069,19.210054397583,2.67489433288574}, {14.9949998855591,18.2680549621582,2.67489433288574}, - {15.0029993057251,19.1580543518066,2.67489433288574}, {15.2369995117188,19.2760543823242,2.67489433288574}, {15.3779993057251,19.4060554504395,2.67489433288574}, {15.4539995193481,19.520055770874,2.67489433288574}, - {15.471999168396,19.52805519104,2.67489433288574}, {15.5449991226196,19.5830554962158,2.67489433288574}, {15.6529998779297,19.573055267334,2.67489433288574}, {15.7059993743896,17.8360557556152,2.67489433288574}, - {15.9449996948242,18.5560550689697,2.67489433288574}, {15.8589992523193,18.9380550384521,2.67489433288574}, {14.9589996337891,18.2950553894043,2.67489433288574}, {15.7779998779297,19.5100555419922,2.67489433288574}, - {14.0049991607666,20.2750549316406,2.67489433288574}, {12.3489990234375,20.5000553131104,2.67489433288574}, {13.0689992904663,19.0150547027588,2.67489433288574}, {13.0999994277954,19.0100555419922,2.67489433288574}, - {15.9489994049072,19.3670558929443,9.41489505767822}, {15.9489994049072,19.2490558624268,9.41489505767822}, {15.75,17.8080558776855,9.41489505767822}, {15.6639995574951,19.5710544586182,9.41489505767822}, - {15.5709991455078,17.9260559082031,9.41489505767822}, {15.8769998550415,18.690055847168,9.41489505767822}, {15.8499994277954,18.8170547485352,9.41489505767822}, {15.9459991455078,18.5520553588867,9.41489505767822}, - {15.914999961853,17.6890544891357,9.41489505767822}, {15.3999996185303,19.4290542602539,9.41489505767822}, {15.3099994659424,19.339054107666,9.41489505767822}, {15.3729991912842,18.0440559387207,9.41489505767822}, - {15.4579992294312,19.5170555114746,9.41489505767822}, {15.5469999313354,19.5820541381836,9.41489505767822}, {13.2309989929199,19.7610549926758,9.41489505767822}, {13.168999671936,19.7360553741455,9.41489505767822}, - {13.096999168396,19.0140552520752,9.41489505767822}, {13.1999988555908,18.9870548248291,9.41489505767822}, {15.1399993896484,19.2080554962158,9.41489505767822}, {15.0159997940063,19.1600551605225,9.41489505767822}, - {14.9859991073608,18.2770557403564,9.41489505767822}, {15.1749992370605,18.1690559387207,9.41489505767822}, {15.9039993286133,19.1320552825928,9.41489505767822}, {15.8949995040894,19.4460544586182,9.41489505767822}, - {15.8769998550415,19.0420551300049,9.41489505767822}, {12.2169990539551,20.6500549316406,9.41489505767822}, {11.9379997253418,20.6810550689697,9.41489505767822}, {11.8629989624023,19.2130546569824,9.41489505767822}, {12.096999168396,19.1950550079346,9.41489505767822}, {14.1669998168945,18.6640548706055,9.41489505767822}, {14.1039991378784,20.2460556030273,9.41489505767822}, {13.9849996566772,18.7360553741455,9.41489505767822}, {14.7349996566772,19.1590557098389,9.41489505767822}, {14.5849990844727,19.2050552368164,9.41489505767822}, {14.5719995498657,18.4850559234619,9.41489505767822}, {14.1939992904663,19.6760559082031,9.41489505767822}, {14.1849994659424,19.9330558776855,9.41489505767822}, {14.1759996414185,18.6640548706055,9.41489505767822}, {14.261999130249,19.4890556335449,9.41489505767822}, {14.3539991378784,19.3610553741455,9.41489505767822}, {14.3559989929199,18.5830554962158,9.41489505767822}, {11.6039991378784,20.1250553131104,9.41489505767822}, {11.5209999084473,20.0520553588867,9.41489505767822}, {11.4209995269775,19.2480545043945,9.41489505767822}, {11.6989994049072,20.2690544128418,9.41389465332031}, {11.7609996795654,20.4310550689697,9.41489505767822}, {11.8359994888306,19.2130546569824,9.41489505767822}, {14.1889991760254,20.1710548400879,9.41489505767822}, {13.9689998626709,20.2840557098389,9.41489505767822}, {13.8739995956421,20.315055847168,9.41489505767822}, {13.7799997329712,18.8080558776855,9.41489505767822}, {13.9869995117188,20.2750549316406,9.41489505767822}, {12.3129997253418,20.5980548858643,9.41489505767822}, {12.3399991989136,20.5090560913086,9.41489505767822}, {12.3489990234375,20.3830547332764,9.41489505767822}, {12.3599996566772,20.2680549621582,9.41489505767822}, {12.3849992752075,20.1850547790527,9.41489505767822}, {12.3849992752075,20.1670551300049,9.41489505767822}, {12.4249992370605,20.065055847168,9.41489505767822}, {12.4729995727539,19.1350555419922,9.41489505767822}, {14.4399995803833,19.2900543212891,9.41489505767822}, {14.3649997711182,18.5740547180176,9.41489505767822}, {13.5729999542236,20.0310554504395,9.41489505767822}, {13.4889993667603,19.9140548706055,9.41489505767822}, {13.5639991760254,18.8710556030273,9.41489505767822}, {13.6389999389648,20.1310558319092,9.41489505767822}, {13.6719989776611,20.2130546569824,9.41489505767822}, {13.75,20.3020553588867,9.41489505767822}, {12.7399997711182,19.7810554504395,9.41489505767822}, {12.6189994812012,19.8520545959473,9.41489505767822}, {12.5799999237061,19.1200542449951,9.41489505767822}, {12.8349990844727,19.069055557251,9.41489505767822}, {11.2669992446899,19.9350547790527,9.41489505767822}, {11.1029987335205,19.9230556488037,9.41489505767822}, {11.0209999084473,19.2600555419922,9.41489505767822}, {11.3819999694824,19.9710559844971,9.41489505767822}, {13.418999671936,19.8530559539795,9.41489505767822}, {13.4329996109009,18.9160556793213,9.41489505767822}, {11.8399991989136,20.6430549621582,9.41489505767822}, {13.3119993209839,19.7800559997559,9.41489505767822}, {15.2189998626709,19.2600555419922,9.41489505767822}, {15.1839990615845,18.1600551605225,9.41489505767822}, {15.3639993667603,18.0520553588867,9.41489505767822}, {13.0189990997314,19.7250556945801,9.41489505767822}, {12.8949995040894,19.7350559234619,9.41489505767822}, {15.9039993286133,19.1500549316406,9.41489505767822}, {15.7699995040894,19.5140552520752,9.41489505767822}, {15.8589992523193,18.9340553283691,9.41489505767822}, {14.1939992904663,19.9510555267334,9.41489505767822}, {14.2119998931885,20.0630550384521,9.41489505767822}, {14.8589992523193,19.149055480957,9.41489505767822}, {14.8159999847412,18.3670558929443,9.41489505767822}, {14.8959999084473,18.3220558166504,9.41489505767822}, {12.5189990997314,19.9360542297363,9.41489505767822}, {11.0209999084473,19.9290542602539,9.41489505767822}, {11.0209999084473,19.2530555725098,2.67489433288574}, {11.0209999084473,19.9300556182861,2.67489433288574}, {15.9799995422363,18.505931854248,5.58724021911621}, {15.9799995422363,18.5044555664062,9.41489505767822}, {15.9799995422363,18.5041732788086,2.67489433288574}, {15.9799995422363,18.1684837341309,2.67489433288574}, {15.9799995422363,18.1288299560547,9.41489505767822}, {15.9799995422363,17.9876575469971,2.67489433288574}, {15.9799995422363,17.6247596740723,3.91620373725891}, {15.9799995422363,17.6247596740723,2.67489433288574}, {15.9799995422363,17.6254329681396,4.32245063781738}, {15.9799995422363,17.8920269012451,9.41489505767822}, {15.9799995422363,17.8795108795166,2.67489433288574}, {15.9799995422363,17.629810333252,4.58585262298584}, {15.9799995422363,17.6336059570312,5.27938556671143}, {15.9799995422363,17.8311748504639,2.67489433288574}, {15.9799995422363,17.638355255127,9.41489505767822}, {15.9799995422363,17.6346111297607,5.98653984069824}, {15.9799995422363,17.8728256225586,2.67489433288574}, {15.9799995422363,18.2221603393555,2.67489433288574} }, - { {0,1,2}, {0,3,1}, {0,4,3}, {5,6,7}, {8,6,5}, {2,9,0}, {6,10,11}, {12,13,14}, {15,16,17}, {18,16,15}, {19,20,21}, {22,23,24}, {25,23,22}, {26,23,25}, {27,23,26}, {28,23,27}, {29,30,31}, {29,32,30}, {29,28,32}, {33,28,29}, {33,23,28}, {21,23,33}, {20,23,21}, {34,35,36}, {37,35,34}, {38,35,37}, {39,35,38}, {40,35,39}, {41,35,40}, {42,35,41}, {43,35,42}, {44,35,43}, {45,35,44}, {46,35,45}, {47,35,46}, {48,35,47}, {49,50,51}, {52,48,47}, {23,49,24}, {53,54,55}, {56,57,58}, {59,57,56}, {60,57,59}, {61,57,60}, {62,57,61}, {63,57,62}, {64,57,63}, {65,57,64}, {66,57,65}, {13,57,66}, {54,67,55}, {68,69,70}, {71,69,68}, {72,69,71}, {73,69,72}, {74,69,73}, {75,69,74}, {76,69,75}, {77,69,76}, {67,69,77}, {70,16,68}, {70,17,16}, {78,79,80}, {81,79,78}, {82,79,81}, {83,79,82}, {84,79,83}, {85,79,84}, {86,79,85}, {87,79,86}, {88,8,5}, {11,7,6}, {11,89,7}, {11,9,89}, {11,0,9}, {55,90,53}, {55,79,90}, {55,80,79}, {91,11,10}, {92,69,12}, {92,70,69}, {34,93,37}, {47,94,52}, {47,95,94}, {47,57,95}, {47,58,57}, {51,24,49}, {21,35,19}, {21,36,35}, {14,92,12}, {86,10,87}, {86,91,10}, {77,55,67}, {66,14,13}, {96,97,4}, {98,99,100}, {101,102,98}, {103,101,98}, {104,103,98}, {105,106,107}, {108,105,107}, {109,108,107}, {100,109,107}, {110,111,112}, {113,110,112}, {114,115,116}, {117,114,116}, {118,119,120}, {121,122,123}, {124,121,123}, {125,126,127}, {128,129,130}, {131,132,133}, {71,131,133}, {134,71,133}, {135,134,133}, {136,135,133}, {137,138,139}, {140,137,139}, {141,140,139}, {142,31,141}, {142,141,139}, {143,126,132}, {144,145,146}, {147,144,146}, {127,147,146}, {148,121,124}, {149,148,124}, {150,149,124}, {151,150,124}, {152,151,124}, {153,152,124}, {154,153,124}, {155,154,124}, {129,156,157}, {130,129,157}, {158,159,160}, {161,158,160}, {162,161,160}, {163,162,160}, {146,163,160}, {164,165,166}, {167,164,166}, {168,169,170}, {171,168,170}, {139,171,170}, {159,172,173}, {123,174,142}, {175,110,113}, {173,175,113}, {106,176,177}, {178,106,177}, {179,180,167}, {112,179,167}, {175,173,172}, {119,118,181}, {119,181,97}, {119,97,96}, {182,98,102}, {182,102,183}, {182,183,120}, {182,120,119}, {143,132,184}, {184,185,143}, {147,127,126}, {174,123,122}, {159,173,160}, {126,125,133}, {126,133,132}, {186,187,188}, {186,188,116}, {186,116,115}, {99,98,182}, {109,100,99}, {106,178,107}, {114,117,177}, {114,177,176}, {128,130,187}, {128,187,186}, {135,136,157}, {135,157,156}, {163,146,145}, {164,167,180}, {179,112,111}, {171,139,138}, {189,155,166}, {189,166,165}, {149,150,93}, {154,155,189}, {31,142,174}, {114,176,78}, {81,78,176}, {7,89,183}, {89,9,120}, {89,120,183}, {78,80,114}, {176,106,81}, {88,5,103}, {183,102,7}, {118,120,9}, {9,2,181}, {9,181,118}, {115,114,80}, {82,81,106}, {101,103,5}, {102,101,5}, {5,7,102}, {97,181,2}, {2,1,97}, {1,3,97}, {80,55,115}, {172,159,59}, {59,56,172}, {3,4,97}, {4,0,96}, {105,108,82}, {186,115,55}, {82,106,105}, {83,82,108}, {60,59,159}, {175,172,56}, {119,96,0}, {0,11,119}, {108,109,84}, {84,83,108}, {55,77,186}, {56,58,110}, {56,110,175}, {60,159,158}, {11,91,182}, {182,119,11}, {91,86,182}, {85,84,109}, {86,85,99}, {128,186,77}, {58,111,110}, {158,161,60}, {26,25,137}, {138,137,25}, {99,182,86}, {109,99,85}, {77,76,128}, {58,47,111}, {61,60,161}, {137,140,26}, {27,26,140}, {25,22,138}, {129,128,76}, {76,75,129}, {75,74,129}, {74,73,156}, {73,72,135}, {68,16,184}, {68,184,132}, {16,18,185}, {161,162,62}, {62,61,161}, {179,111,47}, {171,138,22}, {156,129,74}, {135,156,73}, {134,135,72}, {72,71,134}, {68,132,131}, {185,184,16}, {18,15,185}, {63,62,162}, {28,27,140}, {22,24,171}, {71,68,131}, {15,17,143}, {15,143,185}, {17,70,143}, {70,92,126}, {162,163,64}, {64,63,162}, {180,179,47}, {47,46,180}, {140,141,28}, {168,171,24}, {126,143,70}, {92,14,147}, {147,126,92}, {14,66,144}, {14,144,147}, {65,64,163}, {66,65,145}, {46,45,180}, {32,28,141}, {24,51,168}, {145,144,66}, {163,145,65}, {164,180,45}, {45,44,164}, {44,43,164}, {43,42,165}, {38,37,151}, {150,151,37}, {37,93,150}, {141,31,30}, {30,32,141}, {169,168,51}, {165,164,43}, {189,165,42}, {42,41,189}, {40,39,152}, {40,152,153}, {151,152,39}, {39,38,151}, {93,34,149}, {154,189,41}, {153,154,41}, {41,40,153}, {148,149,34}, {34,36,148}, {36,21,121}, {31,174,29}, {121,148,36}, {21,33,122}, {21,122,121}, {33,29,122}, {174,122,29}, {116,188,53}, {104,98,10}, {87,10,98}, {98,100,87}, {79,87,100}, {79,100,107}, {90,79,107}, {90,107,178}, {178,177,90}, {53,90,177}, {53,177,117}, {117,116,53}, {54,53,188}, {54,188,187}, {67,54,187}, {67,187,130}, {69,67,130}, {69,130,157}, {12,69,157}, {12,157,136}, {136,133,12}, {12,133,125}, {125,127,12}, {13,12,127}, {127,146,13}, {57,13,146}, {57,146,160}, {95,57,160}, {95,160,173}, {173,113,95}, {94,95,113}, {113,112,94}, {52,94,112}, {48,52,112}, {112,167,48}, {35,48,167}, {35,167,166}, {19,35,166}, {139,170,50}, {50,49,139}, {166,155,19}, {20,19,155}, {155,124,20}, {23,20,124}, {23,124,123}, {49,23,123}, {49,123,142}, {142,139,49}, {190,191,170}, {192,191,190}, {191,192,51}, {191,51,50}, {170,169,190}, {169,51,192}, {169,192,190}, {170,191,50}, {193,194,195}, {196,197,198}, {199,200,201}, {198,202,203}, {204,201,200}, {205,204,200}, {206,207,208}, {206,208,205}, {206,205,200}, {207,206,209}, {207,209,203}, {207,203,202}, {202,198,197}, {197,196,210}, {197,210,195}, {197,195,194}, {8,88,195}, {8,195,210}, {210,196,8}, {196,198,8}, {198,203,8}, {203,209,8}, {209,206,8}, {206,200,8}, {202,197,104}, {207,202,104}, {103,104,197}, {103,197,194}, {193,195,88}, {88,103,194}, {88,194,193}, {200,199,8}, {199,201,8}, {204,205,6}, {6,8,201}, {6,201,204}, {10,6,205}, {10,205,208}, {104,10,208}, {104,208,207} }); + { {15.8899993896484f,19.444055557251f,2.67489433288574f}, {15.9129991531372f,19.1590557098389f,2.67489433288574f}, {15.9039993286133f,19.1500549316406f,2.67489433288574f}, {15.9489994049072f,19.2490558624268f,2.67489433288574f}, + {15.9579992294312f,19.3570556640625f,2.67489433288574f}, {15.8819999694824f,18.690055847168f,2.67489433288574f}, {15.8319997787476f,17.7460556030273f,2.67489433288574f}, {15.8489999771118f,18.819055557251f,2.67489433288574f}, + {15.8589992523193f,17.7190551757812f,2.67489433288574f}, {15.8769998550415f,19.0490550994873f,2.67489433288574f}, {15.7529993057251f,17.8080558776855f,2.67489433288574f}, {15.7869997024536f,19.5010547637939f,2.67489433288574f}, + {14.0329990386963f,18.7170543670654f,2.67489433288574f}, {13.9599990844727f,18.7460556030273f,2.67489433288574f}, {13.9869995117188f,20.2840557098389f,2.67489433288574f}, {14.2029991149902f,20.149055480957f,2.67489433288574f}, + {14.1939992904663f,19.9560546875f,2.67489433288574f}, {14.1939992904663f,20.1670551300049f,2.67489433288574f}, {14.2119998931885f,20.0590553283691f,2.67489433288574f}, {12.1899995803833f,19.1840553283691f,2.67489433288574f}, + {12.096999168396f,19.1950550079346f,2.67489433288574f}, {12.1099996566772f,20.6690559387207f,2.67489433288574f}, {11.382999420166f,19.9750556945801f,2.67489433288574f}, {11.2599992752075f,19.2490558624268f,2.67489433288574f}, + {11.2369995117188f,19.9320545196533f,2.67489433288574f}, {11.5349998474121f,20.0640544891357f,2.67489433288574f}, {11.6259994506836f,20.1550559997559f,2.67489433288574f}, {11.6829986572266f,20.2390556335449f,2.67489433288574f}, + {11.7369995117188f,20.3570556640625f,2.67489433288574f}, {11.8449993133545f,20.645055770874f,2.67489433288574f}, {11.7729988098145f,20.4640560150146f,2.67489433288574f}, {11.7799987792969f,20.5370559692383f,9.41389465332031f}, + {11.7639999389648f,20.4470558166504f,2.67489433288574f}, {11.9559993743896f,20.6810550689697f,2.67489433288574f}, {12.3079996109009f,20.6020545959473f,2.67489433288574f}, {12.1959991455078f,19.1860542297363f,2.67489433288574f}, + {12.2059993743896f,20.6540546417236f,2.67489433288574f}, {12.3489990234375f,20.3740558624268f,2.67489433288574f}, {12.3579998016357f,20.2750549316406f,2.67489433288574f}, {12.3669996261597f,20.266056060791f,2.67489433288574f}, + {12.3849992752075f,20.1670551300049f,2.67489433288574f}, {12.4269990921021f,20.0680541992188f,2.67489433288574f}, {12.5029993057251f,19.9540557861328f,2.67489433288574f}, {12.6169996261597f,19.8550548553467f,2.67489433288574f}, + {12.7449989318848f,19.7800559997559f,2.67489433288574f}, {12.7629995346069f,19.7800559997559f,2.67489433288574f}, {12.8799991607666f,19.7350559234619f,2.67489433288574f}, {13.0369997024536f,19.7250556945801f,2.67489433288574f}, + {13.0149993896484f,19.0340557098389f,2.67489433288574f}, {11.1699991226196f,19.2580547332764f,2.67489433288574f}, {11.0959987640381f,19.2580547332764f,2.67489433288574f}, {11.1209993362427f,19.9230556488037f,2.67489433288574f}, + {13.0599994659424f,19.024055480957f,2.67489433288574f}, {14.9049997329712f,18.3170547485352f,2.67489433288574f}, {14.8779993057251f,18.3400554656982f,2.67489433288574f}, {14.8779993057251f,19.149055480957f,2.67489433288574f}, + {13.3039989471436f,19.77805519104f,2.67489433288574f}, {13.1589994430542f,18.9890556335449f,2.67489433288574f}, {13.1559991836548f,19.7350559234619f,2.67489433288574f}, {13.4269990921021f,19.8600559234619f,2.67489433288574f}, + {13.5339994430542f,19.9700546264648f,2.67389440536499f}, {13.6359996795654f,20.1220550537109f,2.67489433288574f}, {13.6359996795654f,20.1400547027588f,2.67489433288574f}, {13.6719989776611f,20.2210559844971f,2.67489433288574f}, + {13.6899995803833f,20.2300548553467f,2.67489433288574f}, {13.7509994506836f,20.3010559082031f,2.67489433288574f}, {13.8539991378784f,20.3180541992188f,2.67489433288574f}, {14.8329992294312f,18.3580551147461f,2.67489433288574f}, + {14.1849994659424f,19.8530559539795f,2.67489433288574f}, {14.0769996643066f,18.7000541687012f,2.67489433288574f}, {14.1099996566772f,20.2400550842285f,2.67489433288574f}, {14.2009992599487f,19.6230545043945f,2.67489433288574f}, + {14.2729997634888f,19.4670543670654f,2.67489433288574f}, {14.3379993438721f,19.3790550231934f,2.67489433288574f}, {14.4549999237061f,19.2770557403564f,2.67489433288574f}, {14.5899991989136f,19.2040557861328f,2.67489433288574f}, + {14.6079998016357f,19.2040557861328f,2.67489433288574f}, {14.7209997177124f,19.1600551605225f,2.67489433288574f}, {15.1379995346069f,19.210054397583f,2.67489433288574f}, {14.9949998855591f,18.2680549621582f,2.67489433288574f}, + {15.0029993057251f,19.1580543518066f,2.67489433288574f}, {15.2369995117188f,19.2760543823242f,2.67489433288574f}, {15.3779993057251f,19.4060554504395f,2.67489433288574f}, {15.4539995193481f,19.520055770874f,2.67489433288574f}, + {15.471999168396f,19.52805519104f,2.67489433288574f}, {15.5449991226196f,19.5830554962158f,2.67489433288574f}, {15.6529998779297f,19.573055267334f,2.67489433288574f}, {15.7059993743896f,17.8360557556152f,2.67489433288574f}, + {15.9449996948242f,18.5560550689697f,2.67489433288574f}, {15.8589992523193f,18.9380550384521f,2.67489433288574f}, {14.9589996337891f,18.2950553894043f,2.67489433288574f}, {15.7779998779297f,19.5100555419922f,2.67489433288574f}, + {14.0049991607666f,20.2750549316406f,2.67489433288574f}, {12.3489990234375f,20.5000553131104f,2.67489433288574f}, {13.0689992904663f,19.0150547027588f,2.67489433288574f}, {13.0999994277954f,19.0100555419922f,2.67489433288574f}, + {15.9489994049072f,19.3670558929443f,9.41489505767822f}, {15.9489994049072f,19.2490558624268f,9.41489505767822f}, {15.75f,17.8080558776855f,9.41489505767822f}, {15.6639995574951f,19.5710544586182f,9.41489505767822f}, + {15.5709991455078f,17.9260559082031f,9.41489505767822f}, {15.8769998550415f,18.690055847168f,9.41489505767822f}, {15.8499994277954f,18.8170547485352f,9.41489505767822f}, {15.9459991455078f,18.5520553588867f,9.41489505767822f}, + {15.914999961853f,17.6890544891357f,9.41489505767822f}, {15.3999996185303f,19.4290542602539f,9.41489505767822f}, {15.3099994659424f,19.339054107666f,9.41489505767822f}, {15.3729991912842f,18.0440559387207f,9.41489505767822f}, + {15.4579992294312f,19.5170555114746f,9.41489505767822f}, {15.5469999313354f,19.5820541381836f,9.41489505767822f}, {13.2309989929199f,19.7610549926758f,9.41489505767822f}, {13.168999671936f,19.7360553741455f,9.41489505767822f}, + {13.096999168396f,19.0140552520752f,9.41489505767822f}, {13.1999988555908f,18.9870548248291f,9.41489505767822f}, {15.1399993896484f,19.2080554962158f,9.41489505767822f}, {15.0159997940063f,19.1600551605225f,9.41489505767822f}, + {14.9859991073608f,18.2770557403564f,9.41489505767822f}, {15.1749992370605f,18.1690559387207f,9.41489505767822f}, {15.9039993286133f,19.1320552825928f,9.41489505767822f}, {15.8949995040894f,19.4460544586182f,9.41489505767822f}, + {15.8769998550415f,19.0420551300049f,9.41489505767822f}, {12.2169990539551f,20.6500549316406f,9.41489505767822f}, {11.9379997253418f,20.6810550689697f,9.41489505767822f}, {11.8629989624023f,19.2130546569824f,9.41489505767822f}, + {12.096999168396f,19.1950550079346f,9.41489505767822f}, {14.1669998168945f,18.6640548706055f,9.41489505767822f}, {14.1039991378784f,20.2460556030273f,9.41489505767822f}, {13.9849996566772f,18.7360553741455f,9.41489505767822f}, + {14.7349996566772f,19.1590557098389f,9.41489505767822f}, {14.5849990844727f,19.2050552368164f,9.41489505767822f}, {14.5719995498657f,18.4850559234619f,9.41489505767822f}, {14.1939992904663f,19.6760559082031f,9.41489505767822f}, + {14.1849994659424f,19.9330558776855f,9.41489505767822f}, {14.1759996414185f,18.6640548706055f,9.41489505767822f}, {14.261999130249f,19.4890556335449f,9.41489505767822f}, {14.3539991378784f,19.3610553741455f,9.41489505767822f}, + {14.3559989929199f,18.5830554962158f,9.41489505767822f}, {11.6039991378784f,20.1250553131104f,9.41489505767822f}, {11.5209999084473f,20.0520553588867f,9.41489505767822f}, {11.4209995269775f,19.2480545043945f,9.41489505767822f}, + {11.6989994049072f,20.2690544128418f,9.41389465332031f}, {11.7609996795654f,20.4310550689697f,9.41489505767822f}, {11.8359994888306f,19.2130546569824f,9.41489505767822f}, {14.1889991760254f,20.1710548400879f,9.41489505767822f}, + {13.9689998626709f,20.2840557098389f,9.41489505767822f}, {13.8739995956421f,20.315055847168f,9.41489505767822f}, {13.7799997329712f,18.8080558776855f,9.41489505767822f}, {13.9869995117188f,20.2750549316406f,9.41489505767822f}, + {12.3129997253418f,20.5980548858643f,9.41489505767822f}, {12.3399991989136f,20.5090560913086f,9.41489505767822f}, {12.3489990234375f,20.3830547332764f,9.41489505767822f}, {12.3599996566772f,20.2680549621582f,9.41489505767822f}, + {12.3849992752075f,20.1850547790527f,9.41489505767822f}, {12.3849992752075f,20.1670551300049f,9.41489505767822f}, {12.4249992370605f,20.065055847168f,9.41489505767822f}, {12.4729995727539f,19.1350555419922f,9.41489505767822f}, + {14.4399995803833f,19.2900543212891f,9.41489505767822f}, {14.3649997711182f,18.5740547180176f,9.41489505767822f}, {13.5729999542236f,20.0310554504395f,9.41489505767822f}, {13.4889993667603f,19.9140548706055f,9.41489505767822f}, + {13.5639991760254f,18.8710556030273f,9.41489505767822f}, {13.6389999389648f,20.1310558319092f,9.41489505767822f}, {13.6719989776611f,20.2130546569824f,9.41489505767822f}, {13.75f,20.3020553588867f,9.41489505767822f}, + {12.7399997711182f,19.7810554504395f,9.41489505767822f}, {12.6189994812012f,19.8520545959473f,9.41489505767822f}, {12.5799999237061f,19.1200542449951f,9.41489505767822f}, {12.8349990844727f,19.069055557251f,9.41489505767822f}, + {11.2669992446899f,19.9350547790527f,9.41489505767822f}, {11.1029987335205f,19.9230556488037f,9.41489505767822f}, {11.0209999084473f,19.2600555419922f,9.41489505767822f}, {11.3819999694824f,19.9710559844971f,9.41489505767822f}, + {13.418999671936f,19.8530559539795f,9.41489505767822f}, {13.4329996109009f,18.9160556793213f,9.41489505767822f}, {11.8399991989136f,20.6430549621582f,9.41489505767822f}, {13.3119993209839f,19.7800559997559f,9.41489505767822f}, + {15.2189998626709f,19.2600555419922f,9.41489505767822f}, {15.1839990615845f,18.1600551605225f,9.41489505767822f}, {15.3639993667603f,18.0520553588867f,9.41489505767822f}, {13.0189990997314f,19.7250556945801f,9.41489505767822f}, + {12.8949995040894f,19.7350559234619f,9.41489505767822f}, {15.9039993286133f,19.1500549316406f,9.41489505767822f}, {15.7699995040894f,19.5140552520752f,9.41489505767822f}, {15.8589992523193f,18.9340553283691f,9.41489505767822f}, + {14.1939992904663f,19.9510555267334f,9.41489505767822f}, {14.2119998931885f,20.0630550384521f,9.41489505767822f}, {14.8589992523193f,19.149055480957f,9.41489505767822f}, {14.8159999847412f,18.3670558929443f,9.41489505767822f}, + {14.8959999084473f,18.3220558166504f,9.41489505767822f}, {12.5189990997314f,19.9360542297363f,9.41489505767822f}, {11.0209999084473f,19.9290542602539f,9.41489505767822f}, {11.0209999084473f,19.2530555725098f,2.67489433288574f}, + {11.0209999084473f,19.9300556182861f,2.67489433288574f}, {15.9799995422363f,18.505931854248f,5.58724021911621f}, {15.9799995422363f,18.5044555664062f,9.41489505767822f}, {15.9799995422363f,18.5041732788086f,2.67489433288574f}, + {15.9799995422363f,18.1684837341309f,2.67489433288574f}, {15.9799995422363f,18.1288299560547f,9.41489505767822f}, {15.9799995422363f,17.9876575469971f,2.67489433288574f}, {15.9799995422363f,17.6247596740723f,3.91620373725891f}, + {15.9799995422363f,17.6247596740723f,2.67489433288574f}, {15.9799995422363f,17.6254329681396f,4.32245063781738f}, {15.9799995422363f,17.8920269012451f,9.41489505767822f}, {15.9799995422363f,17.8795108795166f,2.67489433288574f}, + {15.9799995422363f,17.629810333252f,4.58585262298584f}, {15.9799995422363f,17.6336059570312f,5.27938556671143f}, {15.9799995422363f,17.8311748504639f,2.67489433288574f}, {15.9799995422363f,17.638355255127f,9.41489505767822f}, + {15.9799995422363f,17.6346111297607f,5.98653984069824f}, {15.9799995422363f,17.8728256225586f,2.67489433288574f}, {15.9799995422363f,18.2221603393555f,2.67489433288574f} }, + { {0,1,2}, {0,3,1}, {0,4,3}, {5,6,7}, {8,6,5}, {2,9,0}, {6,10,11}, {12,13,14}, {15,16,17}, {18,16,15}, {19,20,21}, {22,23,24}, {25,23,22}, {26,23,25}, {27,23,26}, {28,23,27}, {29,30,31}, {29,32,30}, {29,28,32}, {33,28,29}, {33,23,28}, {21,23,33}, {20,23,21}, {34,35,36}, {37,35,34}, {38,35,37}, {39,35,38}, {40,35,39}, {41,35,40}, {42,35,41}, {43,35,42}, {44,35,43}, {45,35,44}, {46,35,45}, {47,35,46}, {48,35,47}, {49,50,51}, {52,48,47}, {23,49,24}, {53,54,55}, {56,57,58}, {59,57,56}, {60,57,59}, {61,57,60}, {62,57,61}, {63,57,62}, {64,57,63}, {65,57,64}, {66,57,65}, {13,57,66}, {54,67,55}, {68,69,70}, {71,69,68}, {72,69,71}, {73,69,72}, {74,69,73}, {75,69,74}, {76,69,75}, {77,69,76}, {67,69,77}, {70,16,68}, {70,17,16}, {78,79,80}, {81,79,78}, {82,79,81}, {83,79,82}, {84,79,83}, {85,79,84}, {86,79,85}, {87,79,86}, {88,8,5}, {11,7,6}, {11,89,7}, {11,9,89}, {11,0,9}, {55,90,53}, {55,79,90}, {55,80,79}, {91,11,10}, {92,69,12}, {92,70,69}, {34,93,37}, {47,94,52}, {47,95,94}, {47,57,95}, {47,58,57}, {51,24,49}, {21,35,19}, {21,36,35}, {14,92,12}, {86,10,87}, {86,91,10}, {77,55,67}, {66,14,13}, {96,97,4}, {98,99,100}, {101,102,98}, {103,101,98}, {104,103,98}, {105,106,107}, {108,105,107}, {109,108,107}, {100,109,107}, {110,111,112}, {113,110,112}, {114,115,116}, {117,114,116}, {118,119,120}, {121,122,123}, {124,121,123}, {125,126,127}, {128,129,130}, {131,132,133}, {71,131,133}, {134,71,133}, {135,134,133}, {136,135,133}, {137,138,139}, {140,137,139}, {141,140,139}, {142,31,141}, {142,141,139}, {143,126,132}, {144,145,146}, {147,144,146}, {127,147,146}, {148,121,124}, {149,148,124}, {150,149,124}, {151,150,124}, {152,151,124}, {153,152,124}, {154,153,124}, {155,154,124}, {129,156,157}, {130,129,157}, {158,159,160}, {161,158,160}, {162,161,160}, {163,162,160}, {146,163,160}, {164,165,166}, {167,164,166}, {168,169,170}, {171,168,170}, {139,171,170}, {159,172,173}, {123,174,142}, {175,110,113}, {173,175,113}, {106,176,177}, {178,106,177}, {179,180,167}, {112,179,167}, {175,173,172}, {119,118,181}, {119,181,97}, {119,97,96}, {182,98,102}, {182,102,183}, {182,183,120}, {182,120,119}, {143,132,184}, {184,185,143}, {147,127,126}, {174,123,122}, {159,173,160}, {126,125,133}, {126,133,132}, {186,187,188}, {186,188,116}, {186,116,115}, {99,98,182}, {109,100,99}, {106,178,107}, {114,117,177}, {114,177,176}, {128,130,187}, {128,187,186}, {135,136,157}, {135,157,156}, {163,146,145}, {164,167,180}, {179,112,111}, {171,139,138}, {189,155,166}, {189,166,165}, {149,150,93}, {154,155,189}, {31,142,174}, {114,176,78}, {81,78,176}, {7,89,183}, {89,9,120}, {89,120,183}, {78,80,114}, {176,106,81}, {88,5,103}, {183,102,7}, {118,120,9}, {9,2,181}, {9,181,118}, {115,114,80}, {82,81,106}, {101,103,5}, {102,101,5}, {5,7,102}, {97,181,2}, {2,1,97}, {1,3,97}, {80,55,115}, {172,159,59}, {59,56,172}, {3,4,97}, {4,0,96}, {105,108,82}, {186,115,55}, {82,106,105}, {83,82,108}, {60,59,159}, {175,172,56}, {119,96,0}, {0,11,119}, {108,109,84}, {84,83,108}, {55,77,186}, {56,58,110}, {56,110,175}, {60,159,158}, {11,91,182}, {182,119,11}, {91,86,182}, {85,84,109}, {86,85,99}, {128,186,77}, {58,111,110}, {158,161,60}, {26,25,137}, {138,137,25}, {99,182,86}, {109,99,85}, {77,76,128}, {58,47,111}, {61,60,161}, {137,140,26}, {27,26,140}, {25,22,138}, {129,128,76}, {76,75,129}, {75,74,129}, {74,73,156}, {73,72,135}, {68,16,184}, {68,184,132}, {16,18,185}, {161,162,62}, {62,61,161}, {179,111,47}, {171,138,22}, {156,129,74}, {135,156,73}, {134,135,72}, {72,71,134}, {68,132,131}, {185,184,16}, {18,15,185}, {63,62,162}, {28,27,140}, {22,24,171}, {71,68,131}, {15,17,143}, {15,143,185}, {17,70,143}, {70,92,126}, {162,163,64}, {64,63,162}, {180,179,47}, {47,46,180}, {140,141,28}, {168,171,24}, {126,143,70}, {92,14,147}, {147,126,92}, {14,66,144}, {14,144,147}, {65,64,163}, {66,65,145}, {46,45,180}, {32,28,141}, {24,51,168}, {145,144,66}, {163,145,65}, {164,180,45}, {45,44,164}, {44,43,164}, {43,42,165}, {38,37,151}, {150,151,37}, {37,93,150}, {141,31,30}, {30,32,141}, {169,168,51}, {165,164,43}, {189,165,42}, {42,41,189}, {40,39,152}, {40,152,153}, {151,152,39}, {39,38,151}, {93,34,149}, {154,189,41}, {153,154,41}, {41,40,153}, {148,149,34}, {34,36,148}, {36,21,121}, {31,174,29}, {121,148,36}, {21,33,122}, {21,122,121}, {33,29,122}, {174,122,29}, {116,188,53}, {104,98,10}, {87,10,98}, {98,100,87}, {79,87,100}, {79,100,107}, {90,79,107}, {90,107,178}, {178,177,90}, {53,90,177}, {53,177,117}, {117,116,53}, {54,53,188}, {54,188,187}, {67,54,187}, {67,187,130}, {69,67,130}, {69,130,157}, {12,69,157}, {12,157,136}, {136,133,12}, {12,133,125}, {125,127,12}, {13,12,127}, {127,146,13}, {57,13,146}, {57,146,160}, {95,57,160}, {95,160,173}, {173,113,95}, {94,95,113}, {113,112,94}, {52,94,112}, {48,52,112}, {112,167,48}, {35,48,167}, {35,167,166}, {19,35,166}, {139,170,50}, {50,49,139}, {166,155,19}, {20,19,155}, {155,124,20}, {23,20,124}, {23,124,123}, {49,23,123}, {49,123,142}, {142,139,49}, {190,191,170}, {192,191,190}, {191,192,51}, {191,51,50}, {170,169,190}, {169,51,192}, {169,192,190}, {170,191,50}, {193,194,195}, {196,197,198}, {199,200,201}, {198,202,203}, {204,201,200}, {205,204,200}, {206,207,208}, {206,208,205}, {206,205,200}, {207,206,209}, {207,209,203}, {207,203,202}, {202,198,197}, {197,196,210}, {197,210,195}, {197,195,194}, {8,88,195}, {8,195,210}, {210,196,8}, {196,198,8}, {198,203,8}, {203,209,8}, {209,206,8}, {206,200,8}, {202,197,104}, {207,202,104}, {103,104,197}, {103,197,194}, {193,195,88}, {88,103,194}, {88,194,193}, {200,199,8}, {199,201,8}, {204,205,6}, {6,8,201}, {6,201,204}, {10,6,205}, {10,205,208}, {104,10,208}, {104,208,207} }); break; case TestMesh::pyramid: mesh = TriangleMesh( - { {10,10,40}, {0,0,0}, {20,0,0}, {20,20,0}, {0,20,0} }, + { {10.f,10.f,40.f}, {0.f,0.f,0.f}, {20.f,0.f,0.f}, {20.f,20.f,0.f}, {0.f,20.f,0.f} }, { {0,1,2}, {0,3,4}, {3,1,4}, {1,3,2}, {3,0,2}, {4,1,0} }); break; case TestMesh::two_hollow_squares: diff --git a/tests/fff_print/test_extrusion_entity.cpp b/tests/fff_print/test_extrusion_entity.cpp index 5f7de1f14a..0698f5eb90 100644 --- a/tests/fff_print/test_extrusion_entity.cpp +++ b/tests/fff_print/test_extrusion_entity.cpp @@ -21,7 +21,7 @@ static inline Slic3r::Point random_point(float LO=-50, float HI=50) // build a sample extrusion entity collection with random start and end points. static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50) { - ExtrusionPath t { ExtrusionRole::Perimeter, 1.0, 1.0, 1.0 }; + ExtrusionPath t{ ExtrusionAttributes{ ExtrusionRole::Perimeter, ExtrusionFlow{ 1.0, 1.0, 1.0 } } }; for (size_t j = 0; j < length; ++ j) t.polyline.append(random_point(LO, HI)); return t; @@ -37,9 +37,8 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20 SCENARIO("ExtrusionPath", "[ExtrusionEntity]") { GIVEN("Simple path") { - Slic3r::ExtrusionPath path{ ExtrusionRole::ExternalPerimeter }; - path.polyline = { { 100, 100 }, { 200, 100 }, { 200, 200 } }; - path.mm3_per_mm = 1.; + Slic3r::ExtrusionPath path{ { { 100, 100 }, { 200, 100 }, { 200, 200 } }, + ExtrusionAttributes{ ExtrusionRole::ExternalPerimeter, ExtrusionFlow{ 1., -1.f, -1.f } } }; THEN("first point") { REQUIRE(path.first_point() == path.polyline.front()); } @@ -52,10 +51,7 @@ SCENARIO("ExtrusionPath", "[ExtrusionEntity]") { static ExtrusionPath new_extrusion_path(const Polyline &polyline, ExtrusionRole role, double mm3_per_mm) { - ExtrusionPath path(role); - path.polyline = polyline; - path.mm3_per_mm = 1.; - return path; + return { polyline, ExtrusionAttributes{ role, ExtrusionFlow{ mm3_per_mm, -1.f, -1.f } } }; } SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") @@ -67,6 +63,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), ExtrusionRole::ExternalPerimeter, 1.)); THEN("polygon area") { REQUIRE(loop.polygon().area() == Approx(square.area())); + REQUIRE(loop.area() == Approx(square.area())); } THEN("loop length") { REQUIRE(loop.length() == Approx(square.length())); @@ -110,6 +107,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path(polyline1, ExtrusionRole::ExternalPerimeter, 1.)); loop.paths.emplace_back(new_extrusion_path(polyline2, ExtrusionRole::OverhangPerimeter, 1.)); + THEN("area") { + REQUIRE(loop.area() == Approx(loop.polygon().area())); + } double tot_len = polyline1.length() + polyline2.length(); THEN("length") { REQUIRE(loop.length() == Approx(tot_len)); @@ -212,6 +212,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path(polyline3, ExtrusionRole::ExternalPerimeter, 1.)); loop.paths.emplace_back(new_extrusion_path(polyline4, ExtrusionRole::OverhangPerimeter, 1.)); double len = loop.length(); + THEN("area") { + REQUIRE(loop.area() == Approx(loop.polygon().area())); + } WHEN("splitting at vertex") { Point point(4821067, 9321068); if (! loop.split_at_vertex(point)) @@ -234,6 +237,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") Polyline { { 15896783, 15868739 }, { 24842049, 12117558 }, { 33853238, 15801279 }, { 37591780, 24780128 }, { 37591780, 24844970 }, { 33853231, 33825297 }, { 24842049, 37509013 }, { 15896798, 33757841 }, { 12211841, 24812544 }, { 15896783, 15868739 } }, ExtrusionRole::ExternalPerimeter, 1.)); + THEN("area") { + REQUIRE(loop.area() == Approx(loop.polygon().area())); + } double len = loop.length(); THEN("split_at() preserves total length") { loop.split_at({ 15896783, 15868739 }, false, 0); @@ -378,23 +384,27 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") { REQUIRE(chained == test.chained); ExtrusionEntityCollection unchained_extrusions; extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained, - ExtrusionRole::InternalInfill, 0., 0.4f, 0.3f); + ExtrusionAttributes{ ExtrusionRole::InternalInfill, ExtrusionFlow{ 0., 0.4f, 0.3f } }); THEN("Chaining works") { - ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point); - REQUIRE(chained_extrusions.entities.size() == test.chained.size()); - for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) { + ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point); + REQUIRE(chained_extrusions.size() == test.chained.size()); + for (size_t i = 0; i < chained_extrusions.size(); ++ i) { const Points &p1 = test.chained[i].points; - const Points &p2 = dynamic_cast(chained_extrusions.entities[i])->polyline.points; + Points p2 = chained_extrusions[i].cast()->polyline.points; + if (chained_extrusions[i].flipped()) + std::reverse(p2.begin(), p2.end()); REQUIRE(p1 == p2); } } THEN("Chaining produces no change with no_sort") { unchained_extrusions.no_sort = true; - ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point); - REQUIRE(chained_extrusions.entities.size() == test.unchained.size()); - for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) { + ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point); + REQUIRE(chained_extrusions.size() == test.unchained.size()); + for (size_t i = 0; i < chained_extrusions.size(); ++ i) { const Points &p1 = test.unchained[i].points; - const Points &p2 = dynamic_cast(chained_extrusions.entities[i])->polyline.points; + Points p2 = chained_extrusions[i].cast()->polyline.points; + if (chained_extrusions[i].flipped()) + std::reverse(p2.begin(), p2.end()); REQUIRE(p1 == p2); } } diff --git a/tests/fff_print/test_gcode.cpp b/tests/fff_print/test_gcode.cpp index 34b40d1ff4..3ec1758b4e 100644 --- a/tests/fff_print/test_gcode.cpp +++ b/tests/fff_print/test_gcode.cpp @@ -7,7 +7,7 @@ using namespace Slic3r; SCENARIO("Origin manipulation", "[GCode]") { - Slic3r::GCode gcodegen; + Slic3r::GCodeGenerator gcodegen; WHEN("set_origin to (10,0)") { gcodegen.set_origin(Vec2d(10,0)); REQUIRE(gcodegen.origin() == Vec2d(10, 0)); diff --git a/tests/fff_print/test_gcodewriter.cpp b/tests/fff_print/test_gcodewriter.cpp index 15ffaebb48..35c70ad0c8 100644 --- a/tests/fff_print/test_gcodewriter.cpp +++ b/tests/fff_print/test_gcodewriter.cpp @@ -2,7 +2,7 @@ #include -#include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/GCode/GCodeWriter.hpp" using namespace Slic3r; diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 69ae90178a..90693c8a04 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(${_TEST_NAME}_tests test_aabbindirect.cpp test_kdtreeindirect.cpp test_arachne.cpp + test_arc_welder.cpp test_clipper_offset.cpp test_clipper_utils.cpp test_color.cpp diff --git a/tests/libslic3r/test_arc_welder.cpp b/tests/libslic3r/test_arc_welder.cpp new file mode 100644 index 0000000000..2686216954 --- /dev/null +++ b/tests/libslic3r/test_arc_welder.cpp @@ -0,0 +1,264 @@ +#include +#include + +#include +#include + +using namespace Slic3r; + +TEST_CASE("arc basics", "[ArcWelder]") { + using namespace Slic3r::Geometry; + + WHEN("arc from { 2000.f, 1000.f } to { 1000.f, 2000.f }") { + Vec2f p1{ 2000.f, 1000.f }; + Vec2f p2{ 1000.f, 2000.f }; + float r{ 1000.f }; + THEN("90 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, true); + REQUIRE(is_approx(c, Vec2f{ 1000.f, 1000.f })); + REQUIRE(ArcWelder::arc_angle(p1, p2, r) == Approx(0.5 * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * 0.5 * M_PI).epsilon(0.001)); + } + THEN("90 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, false); + REQUIRE(is_approx(c, Vec2f{ 2000.f, 2000.f })); + } + THEN("270 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, true); + REQUIRE(is_approx(c, Vec2f{ 2000.f, 2000.f })); + REQUIRE(ArcWelder::arc_angle(p1, p2, - r) == Approx(1.5 * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * 1.5 * M_PI).epsilon(0.001)); + } + THEN("270 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, false); + REQUIRE(is_approx(c, Vec2f{ 1000.f, 1000.f })); + } + } + WHEN("arc from { 1707.11f, 1707.11f } to { 1000.f, 2000.f }") { + Vec2f p1{ 1707.11f, 1707.11f }; + Vec2f p2{ 1000.f, 2000.f }; + float r{ 1000.f }; + Vec2f center1 = Vec2f{ 1000.f, 1000.f }; + // Center on the other side of the CCW arch. + Vec2f center2 = center1 + 2. * (0.5 * (p1 + p2) - center1); + THEN("45 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, true); + REQUIRE(is_approx(c, center1, 1.f)); + REQUIRE(ArcWelder::arc_angle(p1, p2, r) == Approx(0.25 * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * 0.25 * M_PI).epsilon(0.001)); + } + THEN("45 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, false); + REQUIRE(is_approx(c, center2, 1.f)); + } + THEN("315 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, true); + REQUIRE(is_approx(c, center2, 1.f)); + REQUIRE(ArcWelder::arc_angle(p1, p2, - r) == Approx((2. - 0.25) * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * (2. - 0.25) * M_PI).epsilon(0.001)); + } + THEN("315 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, false); + REQUIRE(is_approx(c, center1, 1.f)); + } + } + WHEN("arc from { 1866.f, 1500.f } to { 1000.f, 2000.f }") { + Vec2f p1{ 1866.f, 1500.f }; + Vec2f p2{ 1000.f, 2000.f }; + float r{ 1000.f }; + Vec2f center1 = Vec2f{ 1000.f, 1000.f }; + // Center on the other side of the CCW arch. + Vec2f center2 = center1 + 2. * (0.5 * (p1 + p2) - center1); + THEN("60 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, true); + REQUIRE(is_approx(c, center1, 1.f)); + REQUIRE(is_approx(ArcWelder::arc_angle(p1, p2, r), float(M_PI / 3.), 0.001f)); + REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * M_PI / 3.).epsilon(0.001)); + } + THEN("60 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, false); + REQUIRE(is_approx(c, center2, 1.f)); + } + THEN("300 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, true); + REQUIRE(is_approx(c, center2, 1.f)); + REQUIRE(is_approx(ArcWelder::arc_angle(p1, p2, - r), float((2. - 1./3.) * M_PI), 0.001f)); + REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * (2. - 1. / 3.) * M_PI).epsilon(0.001)); + } + THEN("300 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, false); + REQUIRE(is_approx(c, center1, 1.f)); + } + } +} + +TEST_CASE("arc discretization", "[ArcWelder]") { + using namespace Slic3r::Geometry; + WHEN("arc from { 2, 1 } to { 1, 2 }") { + const Point p1 = Point::new_scale(2., 1.); + const Point p2 = Point::new_scale(1., 2.); + const Point center = Point::new_scale(1., 1.); + const float radius = scaled(1.); + const float resolution = scaled(0.002); + auto test = [center, resolution, radius](const Point &p1, const Point &p2, const float r, const bool ccw) { + Vec2f c = ArcWelder::arc_center(p1.cast(), p2.cast(), r, ccw); + REQUIRE((p1.cast() - c).norm() == Approx(radius)); + REQUIRE((c - center.cast()).norm() == Approx(0.)); + Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); + REQUIRE(pts.size() >= 2); + REQUIRE(pts.front() == p1); + REQUIRE(pts.back() == p2); + for (const Point &p : pts) + REQUIRE(std::abs((p.cast() - c.cast()).norm() - double(radius)) < double(resolution + SCALED_EPSILON)); + }; + THEN("90 degrees arc, CCW") { + test(p1, p2, radius, true); + } + THEN("270 degrees arc, CCW") { + test(p2, p1, - radius, true); + } + THEN("90 degrees arc, CW") { + test(p2, p1, radius, false); + } + THEN("270 degrees arc, CW") { + test(p1, p2, - radius, false); + } + } +} + +TEST_CASE("arc fitting", "[ArcWelder]") { + using namespace Slic3r::Geometry; + + WHEN("arc from { 2, 1 } to { 1, 2 }") { + const Point p1 = Point::new_scale(2., 1.); + const Point p2 = Point::new_scale(1., 2.); + const Point center = Point::new_scale(1., 1.); + const float radius = scaled(1.); + const float resolution = scaled(0.002); + auto test = [center, resolution](const Point &p1, const Point &p2, const float r, const bool ccw) { + Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); + ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution); + REQUIRE(path.size() == 2); + REQUIRE(path.front().point == p1); + REQUIRE(path.front().radius == 0.f); + REQUIRE(path.back().point == p2); + REQUIRE(path.back().radius == Approx(r)); + REQUIRE(path.back().ccw() == ccw); + }; + THEN("90 degrees arc, CCW is fitted") { + test(p1, p2, radius, true); + } + THEN("270 degrees arc, CCW is fitted") { + test(p2, p1, - radius, true); + } + THEN("90 degrees arc, CW is fitted") { + test(p2, p1, radius, false); + } + THEN("270 degrees arc, CW is fitted") { + test(p1, p2, - radius, false); + } + } + + WHEN("arc from { 2, 1 } to { 1, 2 }, another arc from { 2, 1 } to { 0, 2 }, tangentially connected") { + const Point p1 = Point::new_scale(2., 1.); + const Point p2 = Point::new_scale(1., 2.); + const Point p3 = Point::new_scale(0., 3.); + const Point center1 = Point::new_scale(1., 1.); + const Point center2 = Point::new_scale(1., 3.); + const float radius = scaled(1.); + const float resolution = scaled(0.002); + auto test = [center1, center2, resolution](const Point &p1, const Point &p2, const Point &p3, const float r, const bool ccw) { + Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); + { + Points pts2 = ArcWelder::arc_discretize(p2, p3, - r, ! ccw, resolution); + REQUIRE(pts.back() == pts2.front()); + pts.insert(pts.end(), std::next(pts2.begin()), pts2.end()); + } + ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution); + REQUIRE(path.size() == 3); + REQUIRE(path.front().point == p1); + REQUIRE(path.front().radius == 0.f); + REQUIRE(path[1].point == p2); + REQUIRE(path[1].radius == Approx(r)); + REQUIRE(path[1].ccw() == ccw); + REQUIRE(path.back().point == p3); + REQUIRE(path.back().radius == Approx(- r)); + REQUIRE(path.back().ccw() == ! ccw); + }; + THEN("90 degrees arches, CCW are fitted") { + test(p1, p2, p3, radius, true); + } + THEN("270 degrees arc, CCW is fitted") { + test(p3, p2, p1, -radius, true); + } + THEN("90 degrees arc, CW is fitted") { + test(p3, p2, p1, radius, false); + } + THEN("270 degrees arc, CW is fitted") { + test(p1, p2, p3, -radius, false); + } + } +} + +TEST_CASE("arc wedge test", "[ArcWelder]") { + using namespace Slic3r::Geometry; + + WHEN("test point inside wedge, arc from { 2, 1 } to { 1, 2 }") { + const int64_t s = 1000000; + const Vec2i64 p1{ 2 * s, s }; + const Vec2i64 p2{ s, 2 * s }; + const Vec2i64 center{ s, s }; + const int64_t radius{ s }; + auto test = [center]( + // Arc data + const Vec2i64 &p1, const Vec2i64 &p2, const int64_t r, const bool ccw, + // Test data + const Vec2i64 &ptest, const bool ptest_inside) { + const Vec2d c = ArcWelder::arc_center(p1.cast(), p2.cast(), double(r), ccw); + REQUIRE(is_approx(c, center.cast())); + REQUIRE(ArcWelder::inside_arc_wedge(p1, p2, center, r > 0, ccw, ptest) == ptest_inside); + REQUIRE(ArcWelder::inside_arc_wedge(p1.cast(), p2.cast(), double(r), ccw, ptest.cast()) == ptest_inside); + }; + auto test_quadrants = [center, test]( + // Arc data + const Vec2i64 &p1, const Vec2i64 &p2, const int64_t r, const bool ccw, + // Test data + const Vec2i64 &ptest1, const bool ptest_inside1, + const Vec2i64 &ptest2, const bool ptest_inside2, + const Vec2i64 &ptest3, const bool ptest_inside3, + const Vec2i64 &ptest4, const bool ptest_inside4) { + test(p1, p2, r, ccw, ptest1 + center, ptest_inside1); + test(p1, p2, r, ccw, ptest2 + center, ptest_inside2); + test(p1, p2, r, ccw, ptest3 + center, ptest_inside3); + test(p1, p2, r, ccw, ptest4 + center, ptest_inside4); + }; + THEN("90 degrees arc, CCW") { + test_quadrants(p1, p2, radius, true, + Vec2i64{ s, s }, true, + Vec2i64{ s, - s }, false, + Vec2i64{ - s, s }, false, + Vec2i64{ - s, - s }, false); + } + THEN("270 degrees arc, CCW") { + test_quadrants(p2, p1, -radius, true, + Vec2i64{ s, s }, false, + Vec2i64{ s, - s }, true, + Vec2i64{ - s, s }, true, + Vec2i64{ - s, - s }, true); + } + THEN("90 degrees arc, CW") { + test_quadrants(p2, p1, radius, false, + Vec2i64{ s, s }, true, + Vec2i64{ s, - s }, false, + Vec2i64{ - s, s }, false, + Vec2i64{ - s, - s }, false); + } + THEN("270 degrees arc, CW") { + test_quadrants(p1, p2, -radius, false, + Vec2i64{ s, s }, false, + Vec2i64{ s, - s }, true, + Vec2i64{ - s, s }, true, + Vec2i64{ - s, - s }, true); + } + } +} diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index d96a2b3e2a..867ff25c62 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -84,16 +84,16 @@ TEST_CASE("Line::perpendicular_to", "[Geometry]") { TEST_CASE("Polygon::contains works properly", "[Geometry]"){ // this test was failing on Windows (GH #1950) Slic3r::Polygon polygon(Points({ - Point(207802834,-57084522), - Point(196528149,-37556190), - Point(173626821,-25420928), - Point(171285751,-21366123), - Point(118673592,-21366123), - Point(116332562,-25420928), - Point(93431208,-37556191), - Point(82156517,-57084523), - Point(129714478,-84542120), - Point(160244873,-84542120) + {207802834,-57084522}, + {196528149,-37556190}, + {173626821,-25420928}, + {171285751,-21366123}, + {118673592,-21366123}, + {116332562,-25420928}, + {93431208,-37556191}, + {82156517,-57084523}, + {129714478,-84542120}, + {160244873,-84542120} })); Point point(95706562, -57294774); REQUIRE(polygon.contains(point)); @@ -196,6 +196,41 @@ TEST_CASE("Offseting a line generates a polygon correctly", "[Geometry]"){ REQUIRE(area.area() == Slic3r::Polygon(Points({Point(10,5),Point(20,5),Point(20,15),Point(10,15)})).area()); } +SCENARIO("Circle Fit, 3 points", "[Geometry]") { + WHEN("Three points make a circle") { + double s1 = scaled(1.); + THEN("circle_center(): A center point { 0, 0 } is returned") { + Vec2d center = Geometry::circle_center(Vec2d{ s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ -s1, 0. }, SCALED_EPSILON); + REQUIRE(is_approx(center, Vec2d(0, 0))); + } + THEN("circle_center(): A center point { 0, 0 } is returned for points in reverse") { + Vec2d center = Geometry::circle_center(Vec2d{ -s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ s1, 0. }, SCALED_EPSILON); + REQUIRE(is_approx(center, Vec2d(0, 0))); + } + THEN("try_circle_center(): A center point { 0, 0 } is returned") { + std::optional center = Geometry::try_circle_center(Vec2d{ s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ -s1, 0. }, SCALED_EPSILON); + REQUIRE(center); + REQUIRE(is_approx(*center, Vec2d(0, 0))); + } + THEN("try_circle_center(): A center point { 0, 0 } is returned for points in reverse") { + std::optional center = Geometry::try_circle_center(Vec2d{ -s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ s1, 0. }, SCALED_EPSILON); + REQUIRE(center); + REQUIRE(is_approx(*center, Vec2d(0, 0))); + } + } + WHEN("Three points are collinear") { + double s1 = scaled(1.); + THEN("circle_center(): A center point { 2, 0 } is returned") { + Vec2d center = Geometry::circle_center(Vec2d{ s1, 0. }, Vec2d{ 2. * s1, 0. }, Vec2d{ 3. * s1, 0. }, SCALED_EPSILON); + REQUIRE(is_approx(center, Vec2d(2. * s1, 0))); + } + THEN("try_circle_center(): Fails for collinear points") { + std::optional center = Geometry::try_circle_center(Vec2d{ s1, 0. }, Vec2d{ 2. * s1, 0. }, Vec2d{ 3. * s1, 0. }, SCALED_EPSILON); + REQUIRE(! center); + } + } +} + SCENARIO("Circle Fit, TaubinFit with Newton's method", "[Geometry]") { GIVEN("A vector of Vec2ds arranged in a half-circle with approximately the same distance R from some point") { Vec2d expected_center(-6, 0); @@ -310,6 +345,7 @@ SCENARIO("Path chaining", "[Geometry]") { GIVEN("A path") { Points points = { Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0) }; THEN("Chained with no diagonals (thus 26 units long)") { + // if chain_points() works correctly, these points should be joined with no diagonal paths std::vector indices = chain_points(points); for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) { double dist = (points.at(indices.at(i)).cast() - points.at(indices.at(i+1)).cast()).norm(); diff --git a/tests/libslic3r/test_polyline.cpp b/tests/libslic3r/test_polyline.cpp index 8271554041..89a84d40a3 100644 --- a/tests/libslic3r/test_polyline.cpp +++ b/tests/libslic3r/test_polyline.cpp @@ -5,6 +5,25 @@ using namespace Slic3r; +SCENARIO("Simplify polyne, template", "[Polyline]") +{ + Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} }; + WHEN("simplified with Douglas-Peucker with back inserter") { + Points out; + douglas_peucker(polyline.begin(), polyline.end(), std::back_inserter(out), 10, [](const Point &p) { return p; }); + THEN("simplified correctly") { + REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} }); + } + } + WHEN("simplified with Douglas-Peucker in place") { + Points out{ polyline }; + out.erase(douglas_peucker(out.begin(), out.end(), out.begin(), 10, [](const Point &p) { return p; }), out.end()); + THEN("simplified correctly") { + REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} }); + } + } +} + SCENARIO("Simplify polyline", "[Polyline]") { GIVEN("polyline 1") { diff --git a/tests/thumbnails/CMakeLists.txt b/tests/thumbnails/CMakeLists.txt new file mode 100644 index 0000000000..e07ef0421f --- /dev/null +++ b/tests/thumbnails/CMakeLists.txt @@ -0,0 +1,13 @@ +get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + +add_executable(${_TEST_NAME}_tests + ${_TEST_NAME}_tests_main.cpp + test_thumbnails_input_string.cpp + test_thumbnails_ini_string.cpp +) + +target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) +set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") + +# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) \ No newline at end of file diff --git a/tests/thumbnails/test_thumbnails_ini_string.cpp b/tests/thumbnails/test_thumbnails_ini_string.cpp new file mode 100644 index 0000000000..32ec5cbd13 --- /dev/null +++ b/tests/thumbnails/test_thumbnails_ini_string.cpp @@ -0,0 +1,235 @@ +#include + +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" + +#include + +using namespace Slic3r; +using namespace GCodeThumbnails; + + +static std::string empty_thumbnails() +{ + return "thumbnails = \n" + "thumbnails_format = "; +} + +static std::string valid_thumbnails() +{ + return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG\n" + "thumbnails_format = JPG"; +} + +static std::string valid_thumbnails2() +{ + return "thumbnails = 160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG\n" + "thumbnails_format = pnG"; +} + +static std::string valid_thumbnails3() +{ + return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG"; +} + +static std::string old_valid_thumbnails() +{ + return "thumbnails = 160x120\n" + "thumbnails_format = JPG"; +} + +static std::string old_valid_thumbnails2() +{ + return "thumbnails = 160x120, 23x78, 320x240\n" + "thumbnails_format = PNG"; +} + +static std::string old_invalid_thumbnails() +{ + return "thumbnails = 160x\n" + "thumbnails_format = JPG"; +} + +static std::string old_invalid_thumbnails2() +{ + return "thumbnails = 160x120, 23*78, 320x240\n" + "thumbnails_format = PNG"; +} + +static std::string out_of_range_thumbnails() +{ + return "thumbnails = 1160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string out_of_range_thumbnails2() +{ + return "thumbnails = 1160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_ext_thumbnails() +{ + return "thumbnails = 1160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = QOI"; +} + +static std::string invalid_ext_thumbnails2() +{ + return "thumbnails = 1160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_val_thumbnails() +{ + return "thumbnails = 1160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = JPG"; +} + +static std::string invalid_val_thumbnails2() +{ + return "thumbnails = x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_val_thumbnails3() +{ + return "thumbnails = 1x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = qoi"; +} + +static std::string invalid_val_thumbnails4() +{ + return "thumbnails = 123*78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = jpG"; +} + +static DynamicPrintConfig thumbnails_config() +{ + DynamicPrintConfig config; + config.apply_only(FullPrintConfig::defaults() , { "thumbnails", "thumbnails_format" }); + + return config; +} + +TEST_CASE("Validate Empty Thumbnails", "[Thumbnails in Config]") { + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config) { + REQUIRE(config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)GCodeThumbnailsFormat::PNG); + }; + + SECTION("Load empty init_data") { + REQUIRE_NOTHROW(config.load_from_ini_string("", Enable)); + test_loaded_config(config); + } + + SECTION("Load empty format and empty thumbnails") { + REQUIRE_THROWS_AS(config.load_from_ini_string(empty_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config); + } +} + +TEST_CASE("Validate New Thumbnails", "[Thumbnails in Config]") { + + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) { + REQUIRE(!config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)format); + }; + + SECTION("Test 1 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails2(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 3 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails3(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (out_of_range)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 2 (out_of_range)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (invalid_ext)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::QOI); + } + + SECTION("Test 2 (invalid_ext)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 3 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails3(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 4 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails4(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } +} + +TEST_CASE("Validate Old Thumbnails", "[Thumbnails in Config]") { + + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) { + REQUIRE(!config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)format); + }; + + SECTION("Test 1 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails2(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 1 (invalid)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (invalid)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } +} + + + + + diff --git a/tests/thumbnails/test_thumbnails_input_string.cpp b/tests/thumbnails/test_thumbnails_input_string.cpp new file mode 100644 index 0000000000..4c623b2736 --- /dev/null +++ b/tests/thumbnails/test_thumbnails_input_string.cpp @@ -0,0 +1,152 @@ +#include +#include + +#include + +using namespace Slic3r; +using namespace GCodeThumbnails; + + +// Test Thumbnails lines + +static std::string empty_thumbnails() +{ + return ""; +} + +static std::string valid_thumbnails() +{ + return "160x120/PNG, 23x78/QOI, 230x780/JPG"; +} + +static std::string valid_thumbnails2() +{ + return "160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG"; +} + +static std::string out_of_range_thumbnail() +{ + return "160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string out_of_range_thumbnail2() +{ + return "160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_ext_thumbnail() +{ + return "160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_ext_thumbnail2() +{ + return "160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail() +{ + return "160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail2() +{ + return "x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail3() +{ + return "x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail4() +{ + return "23*78/QOI, 320x240/PNG, 230x780/JPG"; +} + + +TEST_CASE("Empty Thumbnails", "[Thumbnails]") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(empty_thumbnails()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.empty()); +} + +TEST_CASE("Valid Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails2()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.size() == 4); + } +} + +TEST_CASE("Out of range Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::OutOfRange)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::OutOfRange)); + REQUIRE(thumbnails.size() == 3); + } +} + +TEST_CASE("Invalid extention Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidExt)); + REQUIRE(thumbnails.size() == 4); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidExt)); + REQUIRE(thumbnails.size() == 4); + } +} + +TEST_CASE("Invalid value Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 3") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail3()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 4") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail4()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 2); + } +} \ No newline at end of file diff --git a/tests/thumbnails/thumbnails_tests_main.cpp b/tests/thumbnails/thumbnails_tests_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/thumbnails/thumbnails_tests_main.cpp @@ -0,0 +1 @@ +#include diff --git a/version.inc b/version.inc index 3ba1980d18..b3660d9b23 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.6.1") +set(SLIC3R_VERSION "2.6.2-alpha1") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") -set(SLIC3R_RC_VERSION "2,6,1,0") -set(SLIC3R_RC_VERSION_DOTS "2.6.1.0") +set(SLIC3R_RC_VERSION "2,6,2,0") +set(SLIC3R_RC_VERSION_DOTS "2.6.2.0") diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 3f75617dd5..500cbee836 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -4,7 +4,7 @@ namespace Slic3r { REGISTER_CLASS(ExPolygon, "ExPolygon"); -REGISTER_CLASS(GCode, "GCode"); +REGISTER_CLASS(GCodeGenerator, "GCode"); REGISTER_CLASS(Line, "Line"); REGISTER_CLASS(Polygon, "Polygon"); REGISTER_CLASS(Polyline, "Polyline"); diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index f6365a20a5..d31438c0ac 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -20,15 +20,6 @@ convex_hull(points) OUTPUT: RETVAL -std::vector -chained_path_from(points, start_from) - Points points - Point* start_from - CODE: - RETVAL = chain_points(points, start_from); - OUTPUT: - RETVAL - double rad2deg(angle) double angle diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index 7b1fbb83c7..95c1d2da3d 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -19,7 +19,6 @@ Lines lines(); Clone split_at_vertex(Point* point) %code{% RETVAL = THIS->split_at_vertex(*point); %}; - Clone split_at_index(int index); Clone split_at_first_point(); double length(); double area();