From 94246e93f8b9175e6d9488bf058303d7fae60182 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 14 Oct 2023 19:58:43 +0800 Subject: [PATCH 1/7] Change direction for perimeter extrusion at odd layers. Initial test code based on supermerill/SuperSlicer@87245ae3c1df604fdff502c66a5cbdbf65464ab4 --- src/libslic3r/GCode.cpp | 6 +++++- src/libslic3r/PerimeterGenerator.cpp | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 10663452ea9..21a31d9ba74 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4183,7 +4183,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // next copies (if any) would not detect the correct orientation // extrude all loops ccw - bool was_clockwise = loop.make_counter_clockwise(); + bool was_clockwise = false;//loop.make_counter_clockwise(); + // if spiral vase, we have to ensure that all loops are in the same orientation. + if (m_config.spiral_mode) { + was_clockwise = loop.make_counter_clockwise(); + } // find the point of the loop that is closest to the current extruder position // or randomize if requested diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 209f608c14b..f3a29105e8b 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -380,7 +380,11 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; if (loop.is_contour) { - eloop->make_counter_clockwise(); + if (perimeter_generator.layer_id % 2 == 1) { + eloop->make_clockwise(); + } else { + eloop->make_counter_clockwise(); + } out.append(std::move(children.entities)); out.entities.emplace_back(eloop); } else { @@ -721,8 +725,13 @@ 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(); + if (pg_extrusion.is_contour) { + if (perimeter_generator.layer_id % 2 == 1) { + extrusion_loop.make_clockwise(); + } else { + extrusion_loop.make_counter_clockwise(); + } + } else extrusion_loop.make_clockwise(); From 2f568b45888cf9a684435a387ce3ad3554d9b44b Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sat, 14 Oct 2023 23:56:24 +0800 Subject: [PATCH 2/7] Perimeters for both contours and holes can have alternating direction, to support overhangs on both side. Also fixes wipe on loop. --- src/libslic3r/ExtrusionEntity.hpp | 11 +++++++---- src/libslic3r/GCode.cpp | 24 +++++++++-------------- src/libslic3r/PerimeterGenerator.cpp | 29 ++++++++++++---------------- 3 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 786a99b95ef..9921887b76d 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -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, }; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 21a31d9ba74..765d331e5cd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4182,11 +4182,11 @@ 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 = false;//loop.make_counter_clockwise(); - // if spiral vase, we have to ensure that all loops are in the same orientation. - if (m_config.spiral_mode) { - 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 @@ -4244,26 +4244,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 diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index f3a29105e8b..7cfb97ef828 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -233,9 +233,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 @@ -379,16 +379,15 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime 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(); + } if (loop.is_contour) { - if (perimeter_generator.layer_id % 2 == 1) { - eloop->make_clockwise(); - } else { - 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)); } @@ -723,17 +722,13 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p // Append paths to collection. if (!paths.empty()) { if (extrusion->is_closed) { - ExtrusionLoop extrusion_loop(std::move(paths)); + ExtrusionLoop extrusion_loop(std::move(paths), pg_extrusion.is_contour ? elrDefault : elrHole); // Restore the orientation of the extrusion loop. - if (pg_extrusion.is_contour) { - if (perimeter_generator.layer_id % 2 == 1) { - extrusion_loop.make_clockwise(); - } else { - extrusion_loop.make_counter_clockwise(); - } - } - else + if (perimeter_generator.layer_id % 2 == 1) { extrusion_loop.make_clockwise(); + } else { + 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); From 50b6027243e55660f0e3b2b7b5640874b74f677d Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 15 Oct 2023 21:53:00 +0800 Subject: [PATCH 3/7] 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; From 2d185d82b81262702d819aa7fb78e95efec26f9b Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 15 Oct 2023 22:04:55 +0800 Subject: [PATCH 4/7] Support a special case that treat every layer as steep overhang --- src/libslic3r/PerimeterGenerator.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index ce0bcc73e60..890d6378985 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -229,6 +229,17 @@ static bool detect_steep_overhang(bool is_contour, bool &steep_overhang_contour, bool &steep_overhang_hole) { + // Special case: reverse on every odd layer + if (overhang_steep_width < 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 `overhang_steep_width` From 7c5feb6816cb8f6f996e305a85c8598998859f0f Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 15 Oct 2023 22:47:56 +0800 Subject: [PATCH 5/7] Add options --- src/libslic3r/Layer.cpp | 2 ++ src/libslic3r/PerimeterGenerator.cpp | 36 +++++++++++++-------------- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 21 ++++++++++++++++ src/libslic3r/PrintConfig.hpp | 2 ++ src/libslic3r/PrintObject.cpp | 2 ++ src/slic3r/GUI/ConfigManipulation.cpp | 6 +++++ src/slic3r/GUI/Tab.cpp | 2 ++ 8 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 6a0cf9cfbf1..c120103ae7c 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -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 diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 890d6378985..8fa985a9632 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -21,10 +21,6 @@ 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. @@ -221,16 +217,18 @@ static void lowpass_filter_by_paths_overhang_degree(ExtrusionPaths& paths) { } 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) +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 (overhang_steep_width < EPSILON) { + if (threshold < EPSILON) { if (is_contour) { steep_overhang_contour = true; } else { @@ -242,8 +240,8 @@ static bool detect_steep_overhang(bool is_contour, 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; + // 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))); @@ -270,7 +268,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime Polygon fuzzified; // Detect steep overhangs - bool overhangs_reverse = perimeter_generator.layer_id % 2 == 1 // Only calculate overhang degree on odd layers + bool overhangs_reverse = perimeter_generator.config->overhang_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) { @@ -331,7 +330,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime // 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, + detect_steep_overhang(perimeter_generator.config, loop.is_contour, bbox, extrusion_width, Polygons{polygon}, perimeter_generator.lower_slices, steep_overhang_contour, steep_overhang_hole); } @@ -627,7 +626,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p 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 + bool overhangs_reverse = perimeter_generator.config->overhang_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; @@ -696,7 +696,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p 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, + 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; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index aee4117fd1e..3290c98b988 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -718,7 +718,7 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle) static std::vector 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", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", + "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "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", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 88472ad0e43..433379fef75 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -807,6 +807,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"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 367e594e032..fb235135669 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -828,6 +828,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( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1089e27074e..0fc93facb53 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1084,6 +1084,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" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 8b67342c33a..cbe2da0053f 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -724,6 +724,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" }) toggle_line(el, config->opt_bool("hole_to_polyhole")); + + bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall"); + bool has_overhang_reverse = config->opt_bool("overhang_reverse"); + bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase; + toggle_field("overhang_reverse", allow_overhang_reverse); + toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse); } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index dbadbe40a7c..3b2cc375eb3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1910,6 +1910,8 @@ void TabPrint::build() optgroup->append_single_option_line("reduce_crossing_wall"); optgroup->append_single_option_line("max_travel_detour_distance"); optgroup->append_single_option_line("extra_perimeters_on_overhangs"); + optgroup->append_single_option_line("overhang_reverse"); + optgroup->append_single_option_line("overhang_reverse_threshold"); page = add_options_page(L("Strength"), "empty"); optgroup = page->new_optgroup(L("Walls"), L"param_wall"); From 8abde0fa1aa65658441f59d2e8c6264427816dfe Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Mon, 16 Oct 2023 09:40:36 +0800 Subject: [PATCH 6/7] Disable overhang reverse if vase mode is enabled --- src/slic3r/GUI/ConfigManipulation.cpp | 2 ++ src/slic3r/GUI/Plater.cpp | 2 ++ src/slic3r/Utils/CalibUtils.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index cbe2da0053f..22d93432015 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -275,6 +275,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con config->opt_int("enforce_support_layers") == 0 && config->opt_bool("ensure_vertical_shell_thickness") && ! config->opt_bool("detect_thin_wall") && + ! config->opt_bool("overhang_reverse") && config->opt_enum("timelapse_type") == TimelapseType::tlTraditional)) { wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional.")); @@ -302,6 +303,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0)); new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true)); new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false)); + new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false)); new_conf.set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); sparse_infill_density = 0; timelapse_type = TimelapseType::tlTraditional; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c7619866f49..8a0fd2266a9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -8643,6 +8643,7 @@ void Plater::calib_max_vol_speed(const Calib_Params& params) print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("overhang_reverse", new ConfigOptionBool(false)); print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); print_config->set_key_value("outer_wall_line_width", new ConfigOptionFloatOrPercent(line_width, false)); print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height)); @@ -8738,6 +8739,7 @@ void Plater::calib_VFA(const Calib_Params& params) print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); print_config->set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); print_config->set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config->set_key_value("overhang_reverse", new ConfigOptionBool(false)); print_config->set_key_value("spiral_mode", new ConfigOptionBool(true)); model().objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); model().objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index 55e37dce6a3..d711bac6add 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -656,6 +656,7 @@ void CalibUtils::calib_max_vol_speed(const CalibInfo &calib_info, wxString &erro print_config.set_key_value("top_shell_layers", new ConfigOptionInt(0)); print_config.set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config.set_key_value("overhang_reverse", new ConfigOptionBool(false)); print_config.set_key_value("spiral_mode", new ConfigOptionBool(true)); print_config.set_key_value("outer_wall_line_width", new ConfigOptionFloat(line_width)); print_config.set_key_value("initial_layer_print_height", new ConfigOptionFloat(layer_height)); @@ -719,6 +720,7 @@ void CalibUtils::calib_VFA(const CalibInfo &calib_info, wxString &error_message) print_config.set_key_value("top_shell_layers", new ConfigOptionInt(0)); print_config.set_key_value("bottom_shell_layers", new ConfigOptionInt(1)); print_config.set_key_value("sparse_infill_density", new ConfigOptionPercent(0)); + print_config.set_key_value("overhang_reverse", new ConfigOptionBool(false)); print_config.set_key_value("spiral_mode", new ConfigOptionBool(true)); model.objects[0]->config.set_key_value("brim_type", new ConfigOptionEnum(btOuterOnly)); model.objects[0]->config.set_key_value("brim_width", new ConfigOptionFloat(3.0)); From 88828e5291459ce5d49901d1c446520c3415c418 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Fri, 20 Oct 2023 09:29:17 +0800 Subject: [PATCH 7/7] Allow extrusion reversal and skip overhang detection if fuzzy skin --- src/libslic3r/PerimeterGenerator.cpp | 30 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 78c513adc1f..8a293d71106 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -275,9 +275,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime 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 - && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None; // and not fuzzy skin + 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(); @@ -332,7 +331,15 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime // prepare grown lower layer slices for overhang detection BoundingBox bbox(polygon.points); bbox.offset(SCALED_EPSILON); - + + // 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 (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); @@ -635,9 +642,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p 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 - && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None; // and not fuzzy skin + 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) { @@ -684,7 +690,15 @@ 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); - + + // 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);