From 3e70560ab2b6238ec92dfc53c6c95fc8f6adfa73 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 1 Feb 2025 12:33:54 +0100 Subject: [PATCH] Fix arachne overhang detection --- src/libslic3r/ClipperUtils.cpp | 13 +- src/libslic3r/PerimeterGenerator.cpp | 228 ++++++++++++++++++++++++--- 2 files changed, 217 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 55a3fcaa85b..cac5452e487 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1192,12 +1192,13 @@ ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Paths& subjects, const Cl ClipperLib_Z::PolyTreeToPaths(clipped_polytree, clipped_paths); } - // cleaning: can contain 0-length segments - for (ClipperLib_Z::Path& path : clipped_paths) { - for (size_t i = 1; i < path.size(); i++) { - if ((path[i - 1] - path[i]).squaredNorm() < SCALED_EPSILON) { - path.erase(path.begin() + i); - i--; + // cleaning + for (size_t i_path = 0; i_path < clipped_paths.size(); ++i_path) { + ClipperLib_Z::Path &path = clipped_paths[i_path]; + for (size_t i_pt = 1; i_pt < path.size() - 1; i_pt++) { + if ((path[i_pt - 1] - path[i_pt]).squaredNorm() < SCALED_EPSILON) { + path.erase(path.begin() + i_pt); + i_pt--; } } } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 98885e646a4..18fd2b891fb 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1021,10 +1021,12 @@ void PerimeterGenerator::_sort_overhangs(const Parameters ¶ms, chain_and_reorder_extrusion_paths(paths, &overhang_params.first_point); // merge path that are smaller than epsilon + int nb_erased = 0; for (auto &path : paths) assert(path.length() > SCALED_EPSILON || path.size() == 2); while (paths.size() > 1 && paths.front().size() == 2 && paths.front().length() < coordf_t(SCALED_EPSILON)) { paths[1].polyline.set_front(paths.front().first_point()); paths.erase(paths.begin()); + nb_erased++; } for (size_t idx_path = 1; idx_path < paths.size(); ++idx_path) { ExtrusionPath &path = paths[idx_path]; @@ -1033,8 +1035,9 @@ void PerimeterGenerator::_sort_overhangs(const Parameters ¶ms, // del paths.erase(paths.begin() + idx_path); --idx_path; + nb_erased++; } else { - assert(paths[idx_path-1].last_point() == paths[idx_path].first_point()); + assert(paths[idx_path-1].last_point().coincides_with_epsilon(paths[idx_path].first_point())); } } @@ -1058,6 +1061,9 @@ void PerimeterGenerator::_sort_overhangs(const Parameters ¶ms, } #ifdef _DEBUGINFO + for (size_t idx_path = 1; idx_path < paths.size(); ++idx_path) { + assert(paths[idx_path - 1].last_point() == paths[idx_path].first_point()); + } if (overhang_params.is_loop) { ExtrusionLoop loop_test; loop_test.paths = paths; @@ -1787,6 +1793,77 @@ struct cmpClipperLib_Z { } }; +bool is_length_more_than_epsilon(ClipperLib_Z::Path &path) { + coordf_t length = 0; + for (size_t i = 1; i < path.size(); i++) { + length += (path[i - 1] - path[i]).cast().norm(); + if (length > SCALED_EPSILON) { + return true; + } + } + return false; +} + +bool merge_path(const ClipperLib_Z::Path &tomerge, ClipperLib_Z::Paths &receiver) { +#ifdef _DEBUG + { + // check there seems to be a continous path from start to end + const ClipperLib_Z::Path &path = tomerge; + bool found_another_path_after = false; + bool found_another_path_before = false; + bool found_almost_another_path_after = false; + bool found_almost_another_path_before = false; + int other_paths_count = 0; + for (size_t idx_path2 = 0; idx_path2 < receiver.size(); ++idx_path2) { + other_paths_count++; + found_another_path_after = found_another_path_after || (path.back() == receiver[idx_path2].front()); + found_another_path_before = found_another_path_before || (path.front() == receiver[idx_path2].back()); + found_almost_another_path_after = found_almost_another_path_after || (path.back() - receiver[idx_path2].front()).cast().norm() < SCALED_EPSILON; + found_almost_another_path_before = found_almost_another_path_before || (path.front() - receiver[idx_path2].back()).cast().norm() < SCALED_EPSILON; + } + bool found_another_path_after_strict = found_another_path_after; + bool found_another_path_before_strict = found_another_path_before; + bool found_almost_another_path_after_strict = found_almost_another_path_after; + bool found_almost_another_path_before_strict = found_almost_another_path_before; + //assert(other_paths_count == 0 || found_another_path_after || found_another_path_before); + for (size_t idx_path2 = 0; idx_path2 < receiver.size(); ++idx_path2) { + found_another_path_after = found_another_path_after || path.back() == receiver[idx_path2].front() || path.back() == receiver[idx_path2].back(); + found_another_path_before = found_another_path_before || path.front() == receiver[idx_path2].back() || path.front() == receiver[idx_path2].front(); + found_almost_another_path_after = found_almost_another_path_after || (path.back() - receiver[idx_path2].back()).cast().norm() < SCALED_EPSILON; + found_almost_another_path_before = found_almost_another_path_before || (path.front() - receiver[idx_path2].front()).cast().norm() < SCALED_EPSILON; + } + assert(other_paths_count == 0 || found_another_path_after_strict || found_another_path_before_strict); + } +#endif + size_t idx_first; + bool found_first = false; + // search start + for (idx_first = 0; idx_first < receiver.size(); ++idx_first) { + if (receiver[idx_first].back() == tomerge.front()) { + found_first = true; + receiver[idx_first].insert(receiver[idx_first].end(), tomerge.begin() + 1, tomerge.end()); + break; + } + } + bool found_last = false; + if (found_first) { + //find the last, add it and remove it. + size_t idx_last; + for (idx_last = 0; idx_last < receiver.size(); ++idx_last) { + if (idx_last == idx_first) { + continue; + } + if (receiver[idx_last].front() == receiver[idx_first].back()) { + found_last = true; + receiver[idx_first].insert(receiver[idx_first].end(), receiver[idx_last].begin() + 1, receiver[idx_last].end()); + receiver.erase(receiver.begin() + idx_last); + break; + } + } + } + return found_first && found_last; +} + //TODO: transform to ExtrusionMultiPath instead of ExtrusionPaths ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & params, const ClipperLib_Z::Path &arachne_path, @@ -1854,15 +1931,50 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & #endif dynamic_speed = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctDifference); #ifdef _DEBUG - for (ClipperLib_Z::Path& poly : dynamic_speed) // assert dynamic_speed - for (int i = 0; i < poly.size() - 1; i++) // assert dynamic_speed - assert(poly[i] != poly[i + 1]); // assert dynamic_speed + for (ClipperLib_Z::Path &poly : dynamic_speed) // assert dynamic_speed + for (int i = 0; i < poly.size() - 1; i++) // assert dynamic_speed + assert(poly[i] != poly[i + 1]); // assert dynamic_speed #endif if (!dynamic_speed.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(dynamic_speed, *previous, outer_points); - test_overhangs(*previous, dynamic_speed, outer_points); + test_overhangs(dynamic_speed, *previous, outer_points); + test_overhangs(*previous, dynamic_speed, outer_points); + //for (ClipperLib_Z::Path &poly : dynamic_speed) { + // assert(poly.size() > 1); + // assert(is_length_more_than_epsilon(poly)); + //} + //for (ClipperLib_Z::Path &poly : *previous) { + // assert (poly.size() > 1); + // assert(is_length_more_than_epsilon(poly)); + //} +#endif + // merge epsilon-length from dynamic_speed into previous + for (size_t path_idx = 0; path_idx < dynamic_speed.size(); ++path_idx) { + ClipperLib_Z::Path &poly = dynamic_speed[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + dynamic_speed.erase(dynamic_speed.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, dynamic_speed); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } +#ifdef _DEBUG + for (ClipperLib_Z::Path &poly : dynamic_speed) { + assert(poly.size() > 1); + assert(is_length_more_than_epsilon(poly)); + } + for (ClipperLib_Z::Path &poly : *previous) { + assert (poly.size() > 1); + assert(is_length_more_than_epsilon(poly)); + } #endif previous = &dynamic_speed; } @@ -1887,9 +1999,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!small_speed.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(small_speed, *previous, outer_points); - test_overhangs(*previous, small_speed, outer_points); + test_overhangs(small_speed, *previous, outer_points); + test_overhangs(*previous, small_speed, outer_points); #endif + // merge epsilon-length from small_speed into previous + for (size_t path_idx = 0; path_idx < small_speed.size(); ++path_idx) { + ClipperLib_Z::Path &poly = small_speed[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + small_speed.erase(small_speed.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, small_speed); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &small_speed; } } @@ -1911,9 +2040,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!big_speed.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(big_speed, *previous, outer_points); - test_overhangs(*previous, big_speed, outer_points); + test_overhangs(big_speed, *previous, outer_points); + test_overhangs(*previous, big_speed, outer_points); #endif + // merge epsilon-length from big_speed into previous + for (size_t path_idx = 0; path_idx < big_speed.size(); ++path_idx) { + ClipperLib_Z::Path &poly = big_speed[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + big_speed.erase(big_speed.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, big_speed); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &big_speed; } } @@ -1937,9 +2083,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!small_flow.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(small_flow, *previous, outer_points); - test_overhangs(*previous, small_flow, outer_points); + test_overhangs(small_flow, *previous, outer_points); + test_overhangs(*previous, small_flow, outer_points); #endif + // merge epsilon-length from small_flow into previous + for (size_t path_idx = 0; path_idx < small_flow.size(); ++path_idx) { + ClipperLib_Z::Path &poly = small_flow[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + small_flow.erase(small_flow.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, small_flow); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &small_flow; } } @@ -1961,9 +2124,26 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & if (!big_flow.empty()) { *previous = clip_extrusion(*previous, clipped_zpaths, ClipperLib_Z::ctIntersection); #ifdef _DEBUG - test_overhangs(big_flow, *previous, outer_points); - test_overhangs(*previous, big_flow, outer_points); + test_overhangs(big_flow, *previous, outer_points); + test_overhangs(*previous, big_flow, outer_points); #endif + // merge epsilon-length from big_flow into previous + for (size_t path_idx = 0; path_idx < big_flow.size(); ++path_idx) { + ClipperLib_Z::Path &poly = big_flow[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, *previous); //TODO + big_flow.erase(big_flow.begin() + path_idx); + path_idx--; + } + } + for (size_t path_idx = 0; path_idx < previous->size(); ++path_idx) { + ClipperLib_Z::Path &poly = (*previous)[path_idx]; + if (!is_length_more_than_epsilon(poly)) { + merge_path(poly, big_flow); //TODO + previous->erase(previous->begin() + path_idx); + path_idx--; + } + } previous = &big_flow; } } @@ -1973,10 +2153,20 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & // ensure polylines are valid (at least EPSILON between two points), unless the path is itself shorter than epsilon (then it's two points) for (ClipperLib_Z::Paths *polylines : {&ok_polylines, &dynamic_speed, &small_speed, &big_speed, &small_flow, &big_flow}) { for (ClipperLib_Z::Path &poly : *polylines) { - auto it_end = Slic3r::douglas_peucker(poly.begin(), poly.end(), poly.begin(), double(SCALED_EPSILON), [](const ClipperLib_Z::IntPoint &p) { return Point(p.x(), p.y()); }); - assert(it_end <= poly.end()); - poly.resize(std::distance(poly.begin(), it_end)); - assert(poly.size() >= 2); + if (poly.size() == 1) { + // this polyline can be removed + assert(false); + } else { + assert(poly.size() >= 2); + assert(is_length_more_than_epsilon(poly)); + ClipperLib_Z::Path old_poly = poly; + auto it_end = Slic3r::douglas_peucker( + poly.begin(), poly.end(), poly.begin(), double(SCALED_EPSILON), + [](const ClipperLib_Z::IntPoint &p) { return Point(p.x(), p.y()); }); + assert(it_end <= poly.end()); + poly.resize(std::distance(poly.begin(), it_end)); + assert(poly.size() >= 2); + } } } @@ -2218,6 +2408,8 @@ ExtrusionPaths PerimeterGenerator::create_overhangs_arachne(const Parameters & } bool found_another_path_after_strict = found_another_path_after; bool found_another_path_before_strict = found_another_path_before; + bool found_almost_another_path_after_strict = found_almost_another_path_after; + bool found_almost_another_path_before_strict = found_almost_another_path_before; //assert(other_paths_count == 0 || found_another_path_after || found_another_path_before); for (size_t idx_path2 = 0; idx_path2 < paths.size(); ++idx_path2) { if (idx_path == idx_path2)