Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change direction for perimeter extrusion at odd layers. #2413

Merged
merged 12 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/libslic3r/ExtrusionEntity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,13 @@ enum ExtrusionRole : uint8_t {
};

// Special flags describing loop
enum ExtrusionLoopRole {
elrDefault,
elrContourInternalPerimeter,
elrSkirt,
enum ExtrusionLoopRole : uint8_t {
elrDefault=0x0,
// Loop for the hole, not for the contour
elrHole=0x1,
// Loop that is the most closest to infill
elrInternal = 0x2,
elrSkirt=0x4,
};


Expand Down
22 changes: 10 additions & 12 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4152,8 +4152,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// 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();
bool is_hole = (loop.loop_role() & elrHole) == elrHole;

if (m_config.spiral_mode && !is_hole) {
// if spiral vase, we have to ensure that all contour are in the same orientation.
loop.make_counter_clockwise();
}

// find the point of the loop that is closest to the current extruder position
// or randomize if requested
Expand Down Expand Up @@ -4210,26 +4214,20 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// make a little move inwards before leaving loop
if (m_config.wipe_on_loops.value && paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.wall_loops.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)
// the side depends on the original winding order of the polygon (inwards for contours, outwards 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).
Point a = paths.front().polyline.points[1]; // second point
Point b = *(paths.back().polyline.points.end()-3); // second to last point
if (was_clockwise) {
if (is_hole == loop.is_counter_clockwise()) {
// swap points
Point c = a; a = b; b = c;

// double angle = paths.front().first_point().ccw_angle(a, b) / 3;

// // turn left if contour, turn right if hole
// if (was_clockwise) angle *= -1;

}

double angle = paths.front().first_point().ccw_angle(a, b) / 3;

// turn left if contour, turn right if hole
if (was_clockwise) angle *= -1;
// turn inwards if contour, turn outwards if hole
if (is_hole == loop.is_counter_clockwise()) angle *= -1;

// 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
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ void Layer::make_perimeters()
&& config.gap_infill_speed.value == other_config.gap_infill_speed.value
&& config.filter_out_gap_fill.value == other_config.filter_out_gap_fill.value
&& config.detect_overhang_wall == other_config.detect_overhang_wall
&& config.overhang_reverse == other_config.overhang_reverse
&& config.overhang_reverse_threshold == other_config.overhang_reverse_threshold
&& config.opt_serialize("inner_wall_line_width") == other_config.opt_serialize("inner_wall_line_width")
&& config.opt_serialize("outer_wall_line_width") == other_config.opt_serialize("outer_wall_line_width")
&& config.detect_thin_wall == other_config.detect_thin_wall
Expand Down
158 changes: 140 additions & 18 deletions src/libslic3r/PerimeterGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,61 @@ static void lowpass_filter_by_paths_overhang_degree(ExtrusionPaths& paths) {
}
}

static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls)
template<class _T>
static bool detect_steep_overhang(const PrintRegionConfig *config,
bool is_contour,
const BoundingBox &extrusion_bboxs,
double extrusion_width,
const _T extrusion,
const ExPolygons *lower_slices,
bool &steep_overhang_contour,
bool &steep_overhang_hole)
{
double threshold = config->overhang_reverse_threshold.get_abs_value(extrusion_width);
// Special case: reverse on every odd layer
if (threshold < EPSILON) {
if (is_contour) {
steep_overhang_contour = true;
} else {
steep_overhang_hole = true;
}

return true;
}

Polygons lower_slcier_chopped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, extrusion_bboxs, true);

// All we need to check is whether we have lines outside `threshold`
double off = threshold - 0.5 * extrusion_width;

auto limiton_polygons = offset(lower_slcier_chopped, float(scale_(off)));

auto remain_polylines = diff_pl(extrusion, limiton_polygons);
if (!remain_polylines.empty()) {
if (is_contour) {
steep_overhang_contour = true;
} else {
steep_overhang_hole = true;
}

return true;
}

return false;
}

static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls,
bool &steep_overhang_contour, bool &steep_overhang_hole)
{
// loops is an arrayref of ::Loop objects
// turn each one into an ExtrusionLoop object
ExtrusionEntityCollection coll;
Polygon fuzzified;

// Detect steep overhangs
bool overhangs_reverse = perimeter_generator.config->overhang_reverse &&
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on odd layers

for (const PerimeterGeneratorLoop &loop : loops) {
bool is_external = loop.is_external();
bool is_small_width = loop.is_smaller_width_perimeter;
Expand All @@ -240,9 +289,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
// Note that we set loop role to ContourInternalPerimeter
// also when loop is both internal and external (i.e.
// there's only one contour loop).
loop_role = elrContourInternalPerimeter;
loop_role = elrInternal;
} else {
loop_role = elrDefault;
loop_role = loop.is_contour? elrDefault : elrHole;
}

// detect overhanging/bridging perimeters
Expand Down Expand Up @@ -283,6 +332,22 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
BoundingBox bbox(polygon.points);
bbox.offset(SCALED_EPSILON);

// Always reverse extrusion if use fuzzy skin: https://github.com/SoftFever/OrcaSlicer/pull/2413#issuecomment-1769735357
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why are we differenciate contour and hole for reversing?

Copy link
Collaborator Author

@Noisyfox Noisyfox Oct 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To improve surface quality when only contour/hole surface has steep overhang. There is no need to reverse the hole surface if only the contour surface has steep overhang, and vice versa.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense 👍

if (overhangs_reverse && perimeter_generator.config->fuzzy_skin != FuzzySkinType::None) {
if (loop.is_contour) {
steep_overhang_contour = true;
} else if (perimeter_generator.config->fuzzy_skin != FuzzySkinType::External) {
steep_overhang_hole = true;
}
}
// Detect steep overhang
// Skip the check if we already found steep overhangs
bool found_steep_overhang = (loop.is_contour && steep_overhang_contour) || (!loop.is_contour && steep_overhang_hole);
if (overhangs_reverse && !found_steep_overhang) {
detect_steep_overhang(perimeter_generator.config, loop.is_contour, bbox, extrusion_width, Polygons{polygon}, perimeter_generator.lower_slices,
steep_overhang_contour, steep_overhang_hole);
}

Polylines remain_polines;

//BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning
Expand Down Expand Up @@ -382,16 +447,16 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
} else {
const PerimeterGeneratorLoop &loop = loops[idx.first];
assert(thin_walls.empty());
ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls);
ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls, steep_overhang_contour, steep_overhang_hole);
out.entities.reserve(out.entities.size() + children.entities.size() + 1);
ExtrusionLoop *eloop = static_cast<ExtrusionLoop*>(coll.entities[idx.first]);
coll.entities[idx.first] = nullptr;

eloop->make_counter_clockwise();
if (loop.is_contour) {
eloop->make_counter_clockwise();
out.append(std::move(children.entities));
out.entities.emplace_back(eloop);
} else {
eloop->make_clockwise();
out.entities.emplace_back(eloop);
out.append(std::move(children.entities));
}
Expand Down Expand Up @@ -573,8 +638,13 @@ static void smooth_overhang_level(ExtrusionPaths &paths)
}
}

static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion>& pg_extrusions)
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion>& pg_extrusions,
bool &steep_overhang_contour, bool &steep_overhang_hole)
{
// Detect steep overhangs
bool overhangs_reverse = perimeter_generator.config->overhang_reverse &&
perimeter_generator.layer_id % 2 == 1; // Only calculate overhang degree on odd layers

ExtrusionEntityCollection extrusion_coll;
for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) {
Arachne::ExtrusionLine* extrusion = pg_extrusion.extrusion;
Expand Down Expand Up @@ -621,6 +691,41 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
extrusion_paths_append(temp_paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);

// Always reverse extrusion if use fuzzy skin: https://github.com/SoftFever/OrcaSlicer/pull/2413#issuecomment-1769735357
if (overhangs_reverse && perimeter_generator.config->fuzzy_skin != FuzzySkinType::None) {
if (pg_extrusion.is_contour) {
steep_overhang_contour = true;
} else if (perimeter_generator.config->fuzzy_skin != FuzzySkinType::External) {
steep_overhang_hole = true;
}
}
// Detect steep overhang
// Skip the check if we already found steep overhangs
bool found_steep_overhang = (pg_extrusion.is_contour && steep_overhang_contour) || (!pg_extrusion.is_contour && steep_overhang_hole);
if (overhangs_reverse && !found_steep_overhang) {
std::map<double, ExtrusionPaths> recognization_paths;
for (const ExtrusionPath &path : temp_paths) {
if (recognization_paths.count(path.width))
recognization_paths[path.width].emplace_back(std::move(path));
else
recognization_paths.insert(std::pair<double, ExtrusionPaths>(path.width, {std::move(path)}));
}
for (const auto &it : recognization_paths) {
Polylines be_clipped;

for (const ExtrusionPath &p : it.second) {
be_clipped.emplace_back(std::move(p.polyline));
}

BoundingBox extrusion_bboxs = get_extents(be_clipped);

if (detect_steep_overhang(perimeter_generator.config, pg_extrusion.is_contour, extrusion_bboxs, it.first, be_clipped, perimeter_generator.lower_slices,
steep_overhang_contour, steep_overhang_hole)) {
break;
}
}
}

if (perimeter_generator.config->overhang_speed_classic && perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) {

Flow flow = is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow;
Expand Down Expand Up @@ -728,13 +833,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
// Append paths to collection.
if (!paths.empty()) {
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();

ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole);
extrusion_loop.make_counter_clockwise();
for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) {
assert(it->polyline.points.size() >= 2);
assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
Expand Down Expand Up @@ -1303,6 +1403,23 @@ void PerimeterGenerator::apply_extra_perimeters(ExPolygons &infill_area)
}
}

// Reorient loop direction
static void reorient_perimeters(ExtrusionEntityCollection &entities, bool steep_overhang_contour, bool steep_overhang_hole)
{
if (steep_overhang_hole || steep_overhang_contour) {
for (auto entity : entities) {
if (entity->is_loop()) {
ExtrusionLoop *eloop = static_cast<ExtrusionLoop *>(entity);
// Only reverse when needed
bool need_reverse = ((eloop->loop_role() & elrHole) == elrHole) ? steep_overhang_hole : steep_overhang_contour;
if (need_reverse) {
eloop->make_clockwise();
}
}
}
}
}

void PerimeterGenerator::process_classic()
{
// other perimeters
Expand Down Expand Up @@ -1590,7 +1707,10 @@ void PerimeterGenerator::process_classic()
}
}
// at this point, all loops should be in contours[0]
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls);
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls, steep_overhang_contour, steep_overhang_hole);
reorient_perimeters(entities, steep_overhang_contour, steep_overhang_hole);

// if brim will be printed, reverse the order of perimeters so that
// we continue inwards after having finished the brim
Expand Down Expand Up @@ -2108,11 +2228,13 @@ void PerimeterGenerator::process_arachne()
}
}
}



if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty())
bool steep_overhang_contour = false;
bool steep_overhang_hole = false;
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions, steep_overhang_contour, steep_overhang_hole); !extrusion_coll.empty()) {
reorient_perimeters(extrusion_coll, steep_overhang_contour, steep_overhang_hole);
this->loops->append(extrusion_coll);
}

ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
Expand Down
2 changes: 1 addition & 1 deletion src/libslic3r/Preset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
static std::vector<std::string> s_Preset_print_options {
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
"extra_perimeters_on_overhangs", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall",
"extra_perimeters_on_overhangs", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold",
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
"infill_direction",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
Expand Down
21 changes: 21 additions & 0 deletions src/libslic3r/PrintConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,27 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));

def = this->add("overhang_reverse", coBool);
def->label = L("Reverse on odd");
def->full_label = L("Overhang reversal");
def->category = L("Quality");
def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on odd layers. This alternating pattern can drastically improve steep overhang.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));

def = this->add("overhang_reverse_threshold", coFloatOrPercent);
def->label = L("Reverse threshold");
def->full_label = L("Overhang reversal threshold");
def->category = L("Quality");
def->tooltip = L("Number of mm the overhang need to be for the reversal to be considered useful. Can be a % of the perimeter width."
"\nValue 0 enables reversal on every odd layers regardless.");
def->sidetext = L("mm or %");
def->ratio_over = "line_width";
def->min = 0;
def->max_literal = 20;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(50, true));

def = this->add("overhang_speed_classic", coBool);
def->label = L("Classic mode");
def->category = L("Speed");
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/PrintConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, hole_to_polyhole))
((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold))
((ConfigOptionBool, hole_to_polyhole_twisted))
((ConfigOptionBool, overhang_reverse))
((ConfigOptionFloatOrPercent, overhang_reverse_threshold))
)

PRINT_CONFIG_CLASS_DEFINE(
Expand Down
2 changes: 2 additions & 0 deletions src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,8 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "fuzzy_skin_thickness"
|| opt_key == "fuzzy_skin_point_distance"
|| opt_key == "detect_overhang_wall"
|| opt_key == "overhang_reverse"
|| opt_key == "overhang_reverse_threshold"
//BBS
|| opt_key == "enable_overhang_speed"
|| opt_key == "detect_thin_wall"
Expand Down
Loading