From 50b6027243e55660f0e3b2b7b5640874b74f677d Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 15 Oct 2023 21:53:00 +0800 Subject: [PATCH] Only reverse order if the loops have steep overhang --- src/libslic3r/PerimeterGenerator.cpp | 131 +++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7cfb97ef828..ce0bcc73e60 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -21,6 +21,10 @@ static const double narrow_loop_length_threshold = 10; //we think it's small detail area and will generate smaller line width for it static constexpr double SMALLER_EXT_INSET_OVERLAP_TOLERANCE = 0.22; +// Overhang width greater than this will be consider as steep overhang +// This is a percentage of the extrusion width +static constexpr double overhang_steep_width = 0.5; // TODO: make this configurable + namespace Slic3r { // Hierarchy of perimeters. @@ -216,12 +220,48 @@ 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 +static bool detect_steep_overhang(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) +{ + 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 `overhang_steep_width` + double off = (overhang_steep_width - 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.layer_id % 2 == 1 // Only calculate overhang degree on odd layers + && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None; // and not fuzzy skin + for (const PerimeterGeneratorLoop &loop : loops) { bool is_external = loop.is_external(); bool is_small_width = loop.is_smaller_width_perimeter; @@ -275,6 +315,14 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime // prepare grown lower layer slices for overhang detection BoundingBox bbox(polygon.points); bbox.offset(SCALED_EPSILON); + + // 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(loop.is_contour, bbox, extrusion_width, Polygons{polygon}, perimeter_generator.lower_slices, + steep_overhang_contour, steep_overhang_hole); + } Polylines remain_polines; @@ -375,15 +423,12 @@ 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(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; - if (perimeter_generator.layer_id % 2 == 1) { - eloop->make_clockwise(); - } else { - eloop->make_counter_clockwise(); - } + + eloop->make_counter_clockwise(); if (loop.is_contour) { out.append(std::move(children.entities)); out.entities.emplace_back(eloop); @@ -567,8 +612,13 @@ static void smooth_overhang_level(ExtrusionPaths &paths) } } -static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector& pg_extrusions) +static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector& pg_extrusions, + bool &steep_overhang_contour, bool &steep_overhang_hole) { + // Detect steep overhangs + bool overhangs_reverse = perimeter_generator.layer_id % 2 == 1 // Only calculate overhang degree on odd layers + && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None; // and not fuzzy skin + ExtrusionEntityCollection extrusion_coll; for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) { Arachne::ExtrusionLine* extrusion = pg_extrusion.extrusion; @@ -614,6 +664,33 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p // get non-overhang paths by intersecting this loop with the grown lower slices 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); + + // 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 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(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(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) { @@ -723,13 +800,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p if (!paths.empty()) { if (extrusion->is_closed) { ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole); - // Restore the orientation of the extrusion loop. - if (perimeter_generator.layer_id % 2 == 1) { - extrusion_loop.make_clockwise(); - } else { - extrusion_loop.make_counter_clockwise(); - } - + 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()); @@ -1298,6 +1369,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(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 @@ -1585,7 +1673,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 @@ -2125,11 +2216,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;