Tile the booleans on layers of many pieces

ClipperLib slows down with the number of edges on a scan line, and a
layer cut through a fine relief has tens of thousands of pieces.
detect_surfaces_type ~50 s at 0.1 mm / 2000k, was ~145.
This commit is contained in:
ExPikaPaka
2026-10-02 18:13:05 -03:00
committed by Ian Bassi
parent d905f1a39b
commit 9a86d79038
6 changed files with 181 additions and 110 deletions
+59
View File
@@ -4,6 +4,8 @@
#include <numeric>
#include <unordered_map>
#include <tbb/parallel_for.h>
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "ShortestPath.hpp"
@@ -785,6 +787,63 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
{ return _clipper_ex(ctIntersection, ClipperUtils::SurfacesProvider(subject), ClipperUtils::SurfacesProvider(clip), do_safety_offset); }
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper_ex(ctIntersection, ClipperUtils::SurfacesPtrProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
static Slic3r::ExPolygons clipper_ex_by_piece(ClipType clipType, const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{
// The subject ExPolygons are split into tiles by the centres of their boxes, a few dozen per tile, and each tile is one
// ClipperLib call with the clip cut to the box of the tile's ExPolygons.
BoundingBox extent;
std::vector<BoundingBox> bboxes;
bboxes.reserve(subject.size());
for (const ExPolygon &expoly : subject) {
bboxes.emplace_back(get_extents(expoly));
extent.merge(bboxes.back());
}
const int tiles = std::clamp(int(std::sqrt(double(subject.size()) / 32.)), 1, 32);
std::vector<std::vector<size_t>> members(size_t(tiles * tiles));
std::vector<BoundingBox> tile_bboxes(members.size());
if (extent.defined) {
const Point size = extent.size();
const coord_t tile_w = std::max<coord_t>(1, size.x() / tiles + 1), tile_h = std::max<coord_t>(1, size.y() / tiles + 1);
for (size_t i = 0; i < subject.size(); ++i) {
const Point c = bboxes[i].center();
const size_t tile = size_t(std::clamp(int((c.y() - extent.min.y()) / tile_h), 0, tiles - 1) * tiles +
std::clamp(int((c.x() - extent.min.x()) / tile_w), 0, tiles - 1));
members[tile].emplace_back(i);
tile_bboxes[tile].merge(bboxes[i]);
}
}
std::vector<BoundingBox> clip_bboxes;
clip_bboxes.reserve(clip.size());
for (const Polygon &polygon : clip)
clip_bboxes.emplace_back(get_extents(polygon));
std::vector<Slic3r::ExPolygons> out_tiles(members.size());
tbb::parallel_for(size_t(0), members.size(), [&](size_t tile) {
if (members[tile].empty())
return;
Slic3r::ExPolygons local_subject;
local_subject.reserve(members[tile].size());
for (size_t i : members[tile])
local_subject.emplace_back(subject[i]);
// Grown so that the cut edges of the clip stay clear of the subject, also after the safety offset.
const BoundingBox bbox = tile_bboxes[tile].inflated(SCALED_EPSILON);
Polygons local_clip;
for (size_t i = 0; i < clip.size(); ++i)
if (clip_bboxes[i].overlap(bbox))
if (Polygon clipped = ClipperUtils::clip_clipper_polygon_with_subject_bbox(clip[i], bbox); ! clipped.empty())
local_clip.emplace_back(std::move(clipped));
out_tiles[tile] = _clipper_ex(clipType, ClipperUtils::ExPolygonsProvider(local_subject), ClipperUtils::PolygonsProvider(local_clip), do_safety_offset);
});
Slic3r::ExPolygons out;
for (Slic3r::ExPolygons &out_tile : out_tiles)
append(out, std::move(out_tile));
return out;
}
Slic3r::ExPolygons diff_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return clipper_ex_by_piece(ctDifference, subject, clip, do_safety_offset); }
Slic3r::ExPolygons intersection_ex_by_piece(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return clipper_ex_by_piece(ctIntersection, subject, clip, do_safety_offset); }
// May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative).
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type)
{ return _clipper_ex(ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No, fill_type); }