From 97179d1378100cca28d67cd7d94de4a94b21283e Mon Sep 17 00:00:00 2001 From: "qing.zhang" Date: Wed, 22 Mar 2023 19:09:27 +0800 Subject: [PATCH] ENH: arachne + overhang slow down Signed-off-by: qing.zhang Change-Id: Ic015c1c6d369edabaa1195416942881c0756f839 --- src/libslic3r/ClipperUtils.cpp | 25 ++-- src/libslic3r/ClipperUtils.hpp | 8 +- src/libslic3r/PerimeterGenerator.cpp | 179 ++++++++++++++++++++++++-- src/slic3r/GUI/ConfigManipulation.cpp | 60 ++++----- 4 files changed, 214 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index bbe98890b..ecd0ab4f5 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -63,7 +63,7 @@ Points SinglePathProvider::s_end; // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one // with a set of polygons covering the whole layer below. -template inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox, std::vector &out) +template inline void clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox, std::vector &out, const bool get_entire_polygons=false) { out.clear(); const size_t cnt = src.size(); @@ -97,16 +97,21 @@ template inline void clip_clipper_polygon_with_subject_bbox_ // Never produce just a single point output polygon. if (!out.empty()) - if (int sides_next = sides(out.front()); + if(get_entire_polygons){ + out=src; + }else{ + if (int sides_next = sides(out.front()); // The last point is inside. Take it. sides_this == 0 || // Either this point is outside and previous or next is inside, or // the edge possibly cuts corner of the bounding box. (sides_prev & sides_this & sides_next) == 0) out.emplace_back(src.back()); + } + } - void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } +void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out, get_entire_polygons); } void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) { clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); } template [[nodiscard]] std::vector clip_clipper_polygon_with_subject_bbox_templ(const std::vector &src, const BoundingBox &bbox) @@ -123,10 +128,10 @@ void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBo clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); } -[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox) +[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, const bool get_entire_polygons) { Polygon out; - clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points, get_entire_polygons); return out; } @@ -138,21 +143,21 @@ void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBo out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end()); return out; } -[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox) +[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox, const bool get_entire_polygons) { Polygons out; out.reserve(src.num_contours()); - out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox)); - for (const Polygon &p : src.holes) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox, get_entire_polygons)); + for (const Polygon &p : src.holes) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox, get_entire_polygons)); out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end()); return out; } -[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox) +[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox, const bool get_entire_polygons) { Polygons out; out.reserve(number_polygons(src)); for (const ExPolygon &p : src) { - Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox); + Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox, get_entire_polygons); out.insert(out.end(), temp.begin(), temp.end()); } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index aa9a73dd2..c36778e82 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -305,15 +305,15 @@ namespace ClipperUtils { // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one // with a set of polygons covering the whole layer below. - void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out); + void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons = false); void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out); [[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox); [[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox); void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out); - [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox); + [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, const bool get_entire_polygons = false); [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox); - [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox); - [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox, const bool get_entire_polygons = false); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox, const bool get_entire_polygons = false); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index f938a12b9..9a3a7f759 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -495,6 +495,90 @@ struct PerimeterGeneratorArachneExtrusion bool fuzzify = false; }; + +static void smooth_overhang_level(ExtrusionPaths &paths) +{ + const double threshold_length = scale_(0.8); + const double filter_range = scale_(6.5); + + // 0.save old overhang series first which is input of filter + const int path_num = paths.size(); + if (path_num < 2) + // don't need to do filting if only has one path in vector + return; + std::vector old_overhang_series; + old_overhang_series.reserve(path_num); + for (int i = 0; i < path_num; i++) old_overhang_series.push_back(paths[i].get_overhang_degree()); + + for (int i = 0; i < path_num;) { + if ((paths[i].role() != erPerimeter && paths[i].role() != erExternalPerimeter)) { + i++; + continue; + } + + double current_length = paths[i].length(); + int current_overhang_degree = old_overhang_series[i]; + double total_lens = current_length; + int pt = i + 1; + + for (; pt < path_num; pt++) { + if (paths[pt].get_overhang_degree() != current_overhang_degree || (paths[pt].role() != erPerimeter && paths[pt].role() != erExternalPerimeter)) { + break; + } + total_lens += paths[pt].length(); + } + + if (total_lens < threshold_length) { + double left_total_length = (filter_range - total_lens) / 2; + double right_total_length = left_total_length; + + double temp_length; + int j = i - 1; + int index; + std::vector> neighbor_path; + while (left_total_length > 0) { + index = (j < 0) ? path_num - 1 : j; + if (paths[index].role() == erOverhangPerimeter) break; + temp_length = paths[index].length(); + if (temp_length > left_total_length) + neighbor_path.emplace_back(std::pair(left_total_length, old_overhang_series[index])); + else + neighbor_path.emplace_back(std::pair(temp_length, old_overhang_series[index])); + left_total_length -= temp_length; + j = index; + j--; + } + + j = pt; + while (right_total_length > 0) { + index = j % path_num; + if (paths[index].role() == erOverhangPerimeter) break; + temp_length = paths[index].length(); + if (temp_length > right_total_length) + neighbor_path.emplace_back(std::pair(right_total_length, old_overhang_series[index])); + else + neighbor_path.emplace_back(std::pair(temp_length, old_overhang_series[index])); + right_total_length -= temp_length; + j++; + } + + double sum = 0; + double length_sum = 0; + for (auto it = neighbor_path.begin(); it != neighbor_path.end(); it++) { + sum += (it->first * it->second); + length_sum += it->first; + } + + double average_overhang = (double) (total_lens * current_overhang_degree + sum) / (length_sum + total_lens); + + for (int idx=i; idx& pg_extrusions) { ExtrusionEntityCollection extrusion_coll; @@ -514,26 +598,87 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers && !((perimeter_generator.object_config->enable_support || perimeter_generator.object_config->enforce_support_layers > 0) && perimeter_generator.object_config->support_top_z_distance.value == 0)) { - ClipperLib_Z::Path extrusion_path; extrusion_path.reserve(extrusion->size()); - for (const Arachne::ExtrusionJunction& ej : extrusion->junctions) + BoundingBox extrusion_path_bbox; + for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) { extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w); - - ClipperLib_Z::Paths lower_slices_paths; - lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size()); - for (const Polygon& poly : perimeter_generator.lower_slices_polygons()) { - lower_slices_paths.emplace_back(); - ClipperLib_Z::Path& out = lower_slices_paths.back(); - out.reserve(poly.points.size()); - for (const Point& pt : poly.points) - out.emplace_back(pt.x(), pt.y(), 0); + extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y())); } - // get non-overhang paths by intersecting this loop with the grown lower slices - extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, - is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow); + ClipperLib_Z::Paths lower_slices_paths; + { + lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size()); + Points clipped; + extrusion_path_bbox.offset(SCALED_EPSILON); + for (const Polygon &poly : perimeter_generator.lower_slices_polygons()) { + clipped.clear(); + ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly.points, extrusion_path_bbox, clipped); + if (!clipped.empty()) { + lower_slices_paths.emplace_back(); + ClipperLib_Z::Path &out = lower_slices_paths.back(); + out.reserve(clipped.size()); + for (const Point &pt : clipped) + out.emplace_back(pt.x(), pt.y(), 0); + } + } + } + ExtrusionPaths temp_paths; + // 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); + + if (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; + std::map> clipper_serise; + + 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); + //ExPolygons lower_slcier_chopped = *perimeter_generator.lower_slices; + Polygons lower_slcier_chopped=ClipperUtils::clip_clipper_polygons_with_subject_bbox(*perimeter_generator.lower_slices, extrusion_bboxs, true); + + double start_pos = -it.first * 0.5; + double end_pos = 0.5 * it.first; + + Polylines remain_polylines; + std::vector degree_polygons; + for (int j = 0; j < overhang_sampling_number; j++) { + Polygons limiton_polygons = offset(lower_slcier_chopped, float(scale_(start_pos + (j + 0.5) * (end_pos - start_pos) / (overhang_sampling_number - 1)))); + + Polylines inside_polines = j == 0 ? intersection_pl(be_clipped, limiton_polygons) : intersection_pl(remain_polylines, limiton_polygons); + + remain_polylines = j == 0 ? diff_pl(be_clipped, limiton_polygons) : diff_pl(remain_polylines, limiton_polygons); + + extrusion_paths_append(paths, std::move(inside_polines), j, int(0), role, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height); + + if (remain_polylines.size() == 0) break; + } + + if (remain_polylines.size() != 0) { + extrusion_paths_append(paths, std::move(remain_polylines), overhang_sampling_number - 1, int(0), erOverhangPerimeter, it.second.front().mm3_per_mm, it.second.front().width, it.second.front().height); + } + } + + } else { + paths = std::move(temp_paths); + + } // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter @@ -576,6 +721,12 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p } chain_and_reorder_extrusion_paths(paths, &start_point); + + if (perimeter_generator.config->enable_overhang_speed && perimeter_generator.config->fuzzy_skin == FuzzySkinType::None) { + // BBS: filter the speed + smooth_overhang_level(paths); + } + } } else { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index eb63dd203..2d1a7a48a 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -320,35 +320,35 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } //BBS - if (config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne && - config->opt_bool("enable_overhang_speed")) - { - wxString msg_text = _(L("Arachne engine only works when overhang slowing down is disabled.\n" - "This may cause decline in the quality of overhang surface when print fastly")) + "\n"; - if (is_global_config) - msg_text += "\n" + _(L("Disable overhang slowing down automatically? \n" - "Yes - Enable arachne and disable overhang slowing down\n" - "No - Give up using arachne this time")); - MessageDialog dialog(m_msg_dlg_parent, msg_text, "", - wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); - DynamicPrintConfig new_conf = *config; - is_msg_dlg_already_exist = true; - auto answer = dialog.ShowModal(); - bool enable_overhang_slow_down = true; - if (!is_global_config || answer == wxID_YES) { - new_conf.set_key_value("enable_overhang_speed", new ConfigOptionBool(false)); - enable_overhang_slow_down = false; - } - else { - new_conf.set_key_value("wall_generator", new ConfigOptionEnum(PerimeterGeneratorType::Classic)); - } - apply(config, &new_conf); - if (cb_value_change) { - if (!enable_overhang_slow_down) - cb_value_change("enable_overhang_speed", false); - } - is_msg_dlg_already_exist = false; - } + //if (config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne && + // config->opt_bool("enable_overhang_speed")) + //{ + // wxString msg_text = _(L("Arachne engine only works when overhang slowing down is disabled.\n" + // "This may cause decline in the quality of overhang surface when print fastly")) + "\n"; + // if (is_global_config) + // msg_text += "\n" + _(L("Disable overhang slowing down automatically? \n" + // "Yes - Enable arachne and disable overhang slowing down\n" + // "No - Give up using arachne this time")); + // MessageDialog dialog(m_msg_dlg_parent, msg_text, "", + // wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK)); + // DynamicPrintConfig new_conf = *config; + // is_msg_dlg_already_exist = true; + // auto answer = dialog.ShowModal(); + // bool enable_overhang_slow_down = true; + // if (!is_global_config || answer == wxID_YES) { + // new_conf.set_key_value("enable_overhang_speed", new ConfigOptionBool(false)); + // enable_overhang_slow_down = false; + // } + // else { + // new_conf.set_key_value("wall_generator", new ConfigOptionEnum(PerimeterGeneratorType::Classic)); + // } + // apply(config, &new_conf); + // if (cb_value_change) { + // if (!enable_overhang_slow_down) + // cb_value_change("enable_overhang_speed", false); + // } + // is_msg_dlg_already_exist = false; + //} // BBS int filament_cnt = wxGetApp().preset_bundle->filament_presets.size(); @@ -669,7 +669,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co "min_feature_size", "min_bead_width", "wall_distribution_count" }) toggle_line(el, have_arachne); toggle_field("detect_thin_wall", !have_arachne); - toggle_field("enable_overhang_speed", !have_arachne); + //toggle_field("enable_overhang_speed", !have_arachne); toggle_field("only_one_wall_top", !have_arachne); }