From babc8a88a1dcdfdd7a74445968aa5c3dba444658 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 15 Nov 2022 16:54:26 +0100 Subject: [PATCH] clip_clipper_polygon_with_subject_bbox() and diff_clipped() extracted from TreeSupports to ClipperUtils to be generally available. diff_clipped() is an optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. --- src/libslic3r/ClipperUtils.cpp | 96 ++++++++++++++++++++++++++++++++++ src/libslic3r/ClipperUtils.hpp | 20 +++++++ src/libslic3r/TreeSupport.cpp | 72 +------------------------ 3 files changed, 117 insertions(+), 71 deletions(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 1f83309f4b..7f90fd2f08 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -48,6 +48,100 @@ err: namespace ClipperUtils { 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)); + return out; + } } static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) @@ -537,6 +631,8 @@ Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &cli { return _clipper(ClipperLib::ctDifference, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::SinglePathProvider(clip.points), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } +Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) + { return diff(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 89d05260c4..4017a73f1a 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -24,6 +24,8 @@ using Slic3r::ClipperLib::jtSquare; namespace Slic3r { +class BoundingBox; + static constexpr const float ClipperSafetyOffset = 10.f; static constexpr const Slic3r::ClipperLib::JoinType DefaultJoinType = Slic3r::ClipperLib::jtMiter; @@ -306,6 +308,21 @@ 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); } // offset Polygons @@ -391,6 +408,9 @@ Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &su Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); +// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). +// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. +Slic3r::Polygons diff_clipped(const Slic3r::Polygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index b60744e327..6c5b954065 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -115,76 +115,6 @@ static inline void check_self_intersections(const ExPolygon &expoly, const std:: #endif // _WIN32 } -static inline void clip_for_diff(const Polygon &src, const BoundingBox &bbox, Polygon &out) -{ - out.clear(); - const size_t cnt = src.points.size(); - if (cnt < 3) - return; - - enum class Side { - Left = 1, - Right = 2, - Top = 4, - Bottom = 8 - }; - - auto sides = [bbox](const Point &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.points.back()); - int sides_this = sides(src.points.front()); - const size_t last = cnt - 1; - for (size_t i = 0; i < last; ++ i) { - int sides_next = sides(src.points[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.points.emplace_back(src.points[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; - } - // For the last point, if src is completely outside bbox, then out.points will be empty. Just use the first point instead. - int sides_next = sides(out.points.empty() ? src.points.front() : out.points.front()); - if (// 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.points.emplace_back(src.points.back()); -} - -[[nodiscard]] static inline Polygon clip_for_diff(const Polygon &src, const BoundingBox &bbox) -{ - Polygon out; - clip_for_diff(src, bbox, out); - return out; -} - -[[nodiscard]] static inline Polygons clip_for_diff(const Polygons &src, const BoundingBox &bbox) -{ - Polygons out; - out.reserve(src.size()); - for (const Polygon &p : src) - out.emplace_back(clip_for_diff(p, bbox)); - return out; -} - -[[nodiscard]] static inline Polygons diff_clipped(const Polygons &src, const Polygons &clipping) -{ - return diff(src, clip_for_diff(clipping, get_extents(src).inflated(SCALED_EPSILON))); -} - static constexpr const auto tiny_area_threshold = sqr(scaled(0.001)); static std::vector>> group_meshes(const Print &print, const std::vector &print_object_ids) @@ -821,7 +751,7 @@ static std::optional> polyline_sample_next_point_at_dis Polygons collision_trimmed_buffer; auto collision_trimmed = [&collision_trimmed_buffer, &collision, &ret, distance]() -> const Polygons& { if (collision_trimmed_buffer.empty() && ! collision.empty()) - collision_trimmed_buffer = clip_for_diff(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON)); + collision_trimmed_buffer = ClipperUtils::clip_clipper_polygons_with_subject_bbox(collision, get_extents(ret).inflated(std::max(0, distance) + SCALED_EPSILON)); return collision_trimmed_buffer; };