From 7e163ca6f59c95ca737e4c834fce1e6347ad73b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 1 Nov 2024 10:17:37 +0100 Subject: [PATCH] SPE-2488: Fix slow generation of cancel object polygons by performing union operation of polygons incrementally. When many detailed polygons overlap, performing union over all polygons at once can be quite slow. However, performing the union operation incrementally can be significantly faster in such cases, especially when it is implemented as a parallel reduction. --- src/libslic3r/ClipperUtils.cpp | 20 ++++++++++++++++++++ src/libslic3r/ClipperUtils.hpp | 6 ++++++ src/libslic3r/TriangleMeshSlicer.cpp | 5 ++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 86653598b0..0c36cc3f11 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -20,6 +20,9 @@ #include "libslic3r/Surface.hpp" #include "libslic3r/libslic3r.h" +#include +#include + // #define CLIPPER_UTILS_TIMING #ifdef CLIPPER_UTILS_TIMING @@ -745,6 +748,8 @@ Slic3r::Polygons union_(const Slic3r::Polygons &subject, const ClipperLib::PolyF { return to_polygons(clipper_do(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), fillType, ApplySafetyOffset::No)); } Slic3r::Polygons union_(const Slic3r::ExPolygons &subject) { return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No); } +Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygon &subject2) + { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::SinglePathProvider(subject2.points), ApplySafetyOffset::No); } Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2) { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No); } Slic3r::Polygons union_(Slic3r::Polygons &&subject, const Slic3r::Polygons &subject2) { @@ -1012,6 +1017,21 @@ Polygons union_pt_chained_outside_in(const Polygons &subject) return retval; } +Polygons union_parallel_reduce(const Polygons &subject) +{ + return tbb::parallel_reduce( + tbb::blocked_range(0, subject.size()), Polygons(), + [&subject](tbb::blocked_range range, Polygons partial_union) { + for (size_t subject_idx = range.begin(); subject_idx < range.end(); ++subject_idx) { + partial_union = union_(partial_union, subject[subject_idx]); + } + return partial_union; + }, + [](const Polygons &a, const Polygons &b) { + return union_(a, b); + }); +} + Polygons simplify_polygons(const Polygons &subject) { CLIPPER_UTILS_TIME_LIMIT_MILLIS(CLIPPER_UTILS_TIME_LIMIT_DEFAULT); diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 00931e5268..6a01adef1f 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -520,6 +520,7 @@ inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r:: Slic3r::Polygons union_(const Slic3r::Polygons &subject); Slic3r::Polygons union_(const Slic3r::ExPolygons &subject); Slic3r::Polygons union_(const Slic3r::Polygons &subject, const ClipperLib::PolyFillType fillType); +Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygon &subject2); Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2); Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2); // May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative). @@ -538,6 +539,11 @@ ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject); Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject); +// Perform union operation on Polygons using parallel reduction to merge Polygons one by one. +// When many detailed Polygons overlap, performing union over all Polygons at once can be quite slow. +// However, performing the union operation incrementally can be significantly faster in such cases. +Slic3r::Polygons union_parallel_reduce(const Slic3r::Polygons &subject); + ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes); // Implementing generalized loop (foreach) over a list of nodes which can be diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 81d555aba6..d52d126e7f 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -2434,7 +2434,10 @@ Polygons project_mesh( std::vector top, bottom; std::vector zs { -1e10, 1e10 }; slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, throw_on_cancel); - return union_(top.front(), bottom.back()); + + // We typically perform a union operation on a lot of overlapping polygons, which can be slow in some cases. + // To address this, we use parallel reduction, which can be significantly faster in such cases. + return union_(union_parallel_reduce(top.front()), union_parallel_reduce(bottom.back())); } void cut_mesh(const indexed_triangle_set &mesh, float z, indexed_triangle_set *upper, indexed_triangle_set *lower, bool triangulate_caps)