diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3b5034bdad..cfadc49041 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4437,6 +4437,58 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // extrude along the path std::string gcode; + + // Port of "wipe inside before extruding an external perimeter" feature from super slicer + if (m_config.wipe_before_external_loop.value && !paths.empty() && paths.front().size() > 1 && paths.back().size() > 1 && paths.front().role() == erExternalPerimeter) { + const bool is_full_loop_ccw = loop.polygon().is_counter_clockwise(); + bool is_hole_loop = (loop.loop_role() & ExtrusionLoopRole::elrHole) != 0; // loop.make_counter_clockwise(); + const double nozzle_diam = EXTRUDER_CONFIG(nozzle_diameter); + + // note: previous & next are inverted to extrude "in the opposite direction, and we are "rewinding" + Point previous_point = paths.front().polyline.points[1]; + Point current_point = paths.front().polyline.points.front(); + Point next_point = paths.back().polyline.points.back(); + + // can happen if seam_gap is null + if (next_point == current_point) { + next_point = paths.back().polyline.points[paths.back().polyline.points.size() - 2]; + } + + Point a = next_point; // second point + Point b = previous_point; // second to last point + if ((is_hole_loop ? !is_full_loop_ccw : is_full_loop_ccw)) { + // swap points + std::swap(a, b); + } + + double angle = current_point.ccw_angle(a, b) / 3; + + // turn outwards if contour, turn inwwards if hole + if (is_hole_loop ? !is_full_loop_ccw : is_full_loop_ccw) angle *= -1; + + Vec2d current_pos = current_point.cast(); + Vec2d next_pos = next_point.cast(); + Vec2d vec_dist = next_pos - current_pos; + double vec_norm = vec_dist.norm(); + // Offset distance is the minimum between half the nozzle diameter or half the line width for the upcomming perimeter + // This is to mimimize potential instances where the de-retraction is performed on top of a neighbouring + // thin perimeter due to arachne reducing line width. + coordf_t dist = std::min(scaled(nozzle_diam) * 0.5, scaled(paths.front().width) * 0.5); + + // FIXME Hiding the seams will not work nicely for very densely discretized contours! + Point pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast(); + pt.rotate(angle, current_point); + pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast(); + pt.rotate(angle, current_point); + + // use extrude instead of travel_to_xy to trigger the unretract + ExtrusionPath fake_path_wipe(Polyline{pt, current_point}, paths.front()); + fake_path_wipe.mm3_per_mm = 0; + gcode += extrude_path(fake_path_wipe, "move inwards before retraction/seam", speed); + } + + + bool is_small_peri = false; for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { // description += ExtrusionLoop::role_to_string(loop.loop_role()); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 89f1f49812..dba5cf05e5 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -808,7 +808,7 @@ static std::vector s_Preset_print_options { "small_perimeter_speed", "small_perimeter_threshold","bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface", "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk", "top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap", - "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", + "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "wipe_before_external_loop", "bridge_density", "precise_outer_wall", "overhang_speed_classic", "bridge_acceleration", "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_adaptive_layer_height", "tree_support_auto_brim", "tree_support_brim_width", "gcode_comments", "gcode_label_objects", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ae3df21c86..98d089e1c0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3327,6 +3327,12 @@ def = this->add("filament_loading_speed", coFloats); def->tooltip = L("To minimize the visibility of the seam in a closed loop extrusion, a small inward movement is executed before the extruder leaves the loop."); def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wipe_before_external_loop", coBool); + def->label = L("Wipe before external loop"); + def->tooltip = L("To minimise visibility of potential overextrusion at the start of an external perimeter when printing with Outer/Inner or Inner/Outer/Inner wall print order, the deretraction is performed slightly on the inside from the start of the external perimeter. Thatway any potential over extrusion is hidden from the outside surface. \n\nThis is useful when printing with Outer/Inner or Inner/Outer/Inner wall print order as in these modes it is more likely an external perimeter is printed immediately after a deretraction move."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); def = this->add("wipe_speed", coFloatOrPercent); def->label = L("Wipe speed"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 56fafb8b6e..0af03128d8 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -880,6 +880,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, role_based_wipe_speed)) ((ConfigOptionFloatOrPercent, wipe_speed)) ((ConfigOptionBool, wipe_on_loops)) + ((ConfigOptionBool, wipe_before_external_loop)) ((ConfigOptionEnum, wall_infill_order)) ((ConfigOptionBool, precise_outer_wall)) ((ConfigOptionBool, overhang_speed_classic)) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 8659d2db6b..bdff812112 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1924,6 +1924,7 @@ void TabPrint::build() optgroup->append_single_option_line("role_based_wipe_speed","seam"); optgroup->append_single_option_line("wipe_speed", "seam"); optgroup->append_single_option_line("wipe_on_loops","seam"); + optgroup->append_single_option_line("wipe_before_external_loop","seam"); optgroup = page->new_optgroup(L("Precision"), L"param_precision");