diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index 844c485f10e..993535f594e 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -85,6 +85,8 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_perimeter() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Perimeter); } bool is_external_perimeter() const { return this->is_perimeter() && this->is_external(); } + bool is_internal_perimeter() const { return is_perimeter() && !this->is_external(); } + bool is_overhang_perimeter() const { return this->is_perimeter() && ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Bridge); } 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); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index bde9ffd1052..322ab757837 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2795,7 +2795,7 @@ static inline bool validate_smooth_path(const GCode::SmoothPath &smooth_path, bo static constexpr const double min_gcode_segment_length = 0.002; -std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { // Extrude all loops CCW. bool is_hole = loop_src.is_clockwise(); @@ -2807,7 +2807,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC // 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)); + loop_src, reverse ^ 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; // if polyline was shorter than the clipping distance we'd get a null polyline, so @@ -2854,7 +2854,7 @@ std::string GCodeGenerator::extrude_skirt( { 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)); + loop_src, this->m_layer->id() % 2 == 1, 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 @@ -2912,9 +2912,24 @@ std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entit 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 + else if (const ExtrusionLoop *loop = dynamic_cast(&entity.extrusion_entity())) { + ExtrusionRole role = entity.extrusion_entity().role(); + bool reverse = + this->m_layer->id() % 2 == 1 && + (role.is_internal_perimeter() && this->config().internal_perimeters_reverse || + role.is_infill() && this->config().infill_reverse) + || + role.is_internal_perimeter() && this->config().internal_perimeters_reverse && this->config().perimeters == 2; + + if (this->config().overhangs_reverse) { + for (ExtrusionPath el: loop->paths) + if (el.role().is_overhang_perimeter() && this->m_layer->id() % 2 == 1) { + reverse = true; + break; + } + } + return this->extrude_loop(*loop, reverse, smooth_path_cache, description, speed); + } else throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()"); return {}; } @@ -2945,10 +2960,11 @@ std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &sup 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(&eref.extrusion_entity()); + bool reverse = (this->m_layer->id() % 2 == 1); if (path) - gcode += this->extrude_path(*path, eref.flipped(), smooth_path_cache, label, speed); + gcode += this->extrude_path(*path, reverse ^ 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); + gcode += this->extrude_multi_path(*multipath, reverse ^ eref.flipped(), smooth_path_cache, label, speed); else { const ExtrusionEntityCollection *eec = dynamic_cast(&eref.extrusion_entity()); assert(eec); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 809efe8655b..36c968374f9 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -372,7 +372,7 @@ class GCodeGenerator { const bool spiral_vase_enabled ); 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_loop(const ExtrusionLoop &loop, bool reverse, 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); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 44c29c32c26..5bb39bdf3f6 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -472,7 +472,8 @@ static std::vector s_Preset_print_options { "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", - "wall_distribution_count", "min_feature_size", "min_bead_width" + "wall_distribution_count", "min_feature_size", "min_bead_width", + "overhangs_reverse", "internal_perimeters_reverse", "infill_reverse" }; static std::vector s_Preset_filament_options { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 88ba446eba2..7a4f499a2b0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3482,6 +3482,27 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->set_default_value(new ConfigOptionFloatOrPercent(85, true)); + def = this->add("overhangs_reverse", coBool); + def->label = L("Reverse overhang perimeters directions every layer"); + def->category = L("Advanced"); + def->tooltip = L("Extrude each layer overhang perimeters in opposite direction, improving quality."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("internal_perimeters_reverse", coBool); + def->label = L("Reverse internal perimeter directions every layer"); + def->category = L("Advanced"); + def->tooltip = L("Extrude each layer internal perimeters in opposite direction, reduce internal stress and warping."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("infill_reverse", coBool); + def->label = L("Reverse infill direction every layer"); + def->category = L("Advanced"); + def->tooltip = L("Extrude each layer infill in opposite direction, reduce internal stress and warping."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + // Declare retract values for filament profile, overriding the printer's extruder profile. for (const char *opt_key : { // floats diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5c9bd4ee3cc..44569b5bdea 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -599,6 +599,10 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, thick_bridges)) ((ConfigOptionFloat, xy_size_compensation)) ((ConfigOptionBool, wipe_into_objects)) + //Perimeters reverse + ((ConfigOptionBool, overhangs_reverse)) + ((ConfigOptionBool, internal_perimeters_reverse)) + ((ConfigOptionBool, infill_reverse)) ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index dc5a86fc3bc..3d36d001ea5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -715,7 +715,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "perimeter_extrusion_width" || opt_key == "infill_overlap" || opt_key == "external_perimeters_first" - || opt_key == "arc_fitting") { + || opt_key == "arc_fitting" + || opt_key == "overhangs_reverse" + || opt_key == "internal_perimeters_reverse") { steps.emplace_back(posPerimeters); } else if ( opt_key == "gap_fill_enabled" @@ -826,7 +828,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "infill_anchor" || opt_key == "infill_anchor_max" || opt_key == "top_infill_extrusion_width" - || opt_key == "first_layer_extrusion_width") { + || opt_key == "first_layer_extrusion_width" + || opt_key == "infill_reverse" ) { steps.emplace_back(posInfill); } else if (opt_key == "fill_pattern") { steps.emplace_back(posPrepareInfill); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fe15f36aea3..43969e1247d 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1689,6 +1689,11 @@ void TabPrint::build() optgroup->append_single_option_line("xy_size_compensation"); optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487"); + optgroup = page->new_optgroup(L("Extrusion direction reverse")); + optgroup->append_single_option_line("overhangs_reverse"); + optgroup->append_single_option_line("internal_perimeters_reverse"); + optgroup->append_single_option_line("infill_reverse"); + optgroup = page->new_optgroup(L("Arachne perimeter generator")); optgroup->append_single_option_line("wall_transition_angle"); optgroup->append_single_option_line("wall_transition_filter_deviation");