From fdd659cefeda5cddf3d4042bcd8d5d23e9264787 Mon Sep 17 00:00:00 2001 From: "qing.zhang" Date: Mon, 6 Mar 2023 15:13:36 +0800 Subject: [PATCH] ENH: speed up the wall generator process by using bounding box Signed-off-by: qing.zhang Change-Id: Ifccbb33bc6df28136746f98020ef2cad002b5868 --- src/libslic3r/ClipperUtils.cpp | 92 +++++++++++++++++++++++++++- src/libslic3r/ClipperUtils.hpp | 20 +++++- src/libslic3r/PerimeterGenerator.cpp | 21 ++++--- 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index b7aeed8a6..0745bea8d 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -57,8 +57,96 @@ err: #endif /* CLIPPER_UTILS_DEBUG */ namespace ClipperUtils { - Points EmptyPathsProvider::s_empty_points; - Points SinglePathProvider::s_end; +Points EmptyPathsProvider::s_empty_points; +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) +{ + out.clear(); + const size_t cnt = src.size(); + if (cnt < 3) return; + + enum class Side { Left = 1, Right = 2, Top = 4, Bottom = 8 }; + + auto sides = [bbox](const PointType &p) { + return int(p.x() < bbox.min.x()) * int(Side::Left) + int(p.x() > bbox.max.x()) * int(Side::Right) + int(p.y() < bbox.min.y()) * int(Side::Bottom) + + int(p.y() > bbox.max.y()) * int(Side::Top); + }; + + int sides_prev = sides(src.back()); + int sides_this = sides(src.front()); + const size_t last = cnt - 1; + for (size_t i = 0; i < last; ++i) { + int sides_next = sides(src[i + 1]); + if ( // This 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[i]); + sides_prev = sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore this point. + } + sides_this = sides_next; + } + + // Never produce just a single point output polygon. + if (!out.empty()) + 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 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) +{ + std::vector out; + clip_clipper_polygon_with_subject_bbox(src, bbox, out); + return out; +} + +[[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox) { return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); } +[[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox) { return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); } + +void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out) { + 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) +{ + Polygon out; + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); + return out; +} + +[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox) +{ + Polygons out; + out.reserve(src.size()); + for (const Polygon &p : src) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + 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) +{ + 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.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), out.end()); + return out; +} } static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index d96061b07..0c703b631 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -296,7 +296,25 @@ namespace ClipperUtils { const SurfacesPtr &m_surfaces; size_t m_size; }; -} + + + // For ClipperLib with Z coordinates. + using ZPoint = Vec3i32; + using ZPoints = std::vector; + + // 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 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]] 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); + + } // Perform union of input polygons using the non-zero rule, convert to ExPolygons. ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union = false); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index cb37471e4..ff7d94a6e 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -268,6 +268,10 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) { // get non 100% overhang paths by intersecting this loop with the grown lower slices + // prepare grown lower layer slices for overhang detection + BoundingBox bbox(polygon.points); + bbox.offset(SCALED_EPSILON); + Polylines remain_polines; //BBS: don't calculate overhang degree when enable fuzzy skin. It's unmeaning @@ -275,10 +279,10 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime for (auto it = lower_polygons_series->begin(); it != lower_polygons_series->end(); it++) { + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(it->second, bbox); - Polylines inside_polines = (it == lower_polygons_series->begin()) ? - intersection_pl({ polygon }, it->second) : - intersection_pl(remain_polines, it->second); + Polylines inside_polines = (it == lower_polygons_series->begin()) ? intersection_pl({polygon}, lower_polygons_series_clipped) : + intersection_pl(remain_polines, lower_polygons_series_clipped); extrusion_paths_append( paths, std::move(inside_polines), @@ -289,9 +293,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime extrusion_width, (float)perimeter_generator.layer_height); - remain_polines = (it == lower_polygons_series->begin()) ? - diff_pl({ polygon }, it->second) : - diff_pl(remain_polines, it->second); + remain_polines = (it == lower_polygons_series->begin()) ? diff_pl({polygon}, lower_polygons_series_clipped) : + diff_pl(remain_polines, lower_polygons_series_clipped); if (remain_polines.size() == 0) break; @@ -299,7 +302,9 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime } else { auto it = lower_polygons_series->end(); it--; - Polylines inside_polines = intersection_pl({ polygon }, it->second); + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(it->second, bbox); + + Polylines inside_polines = intersection_pl({polygon}, lower_polygons_series_clipped); extrusion_paths_append( paths, std::move(inside_polines), @@ -310,7 +315,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime extrusion_width, (float)perimeter_generator.layer_height); - remain_polines = diff_pl({ polygon }, it->second); + remain_polines = diff_pl({polygon}, lower_polygons_series_clipped); } // get 100% overhang paths by checking what parts of this loop fall