Improve performance by migrating to Clipper2 2.0.1 (#15969)

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
This commit is contained in:
Ian Bassi
2026-10-02 17:33:41 -03:00
committed by GitHub
co-authored by Rodrigo Faselli
parent 70bc02467b
commit 222c6a2df5
78 changed files with 3028 additions and 7038 deletions
+29 -37
View File
@@ -2,6 +2,7 @@
#include "AABBTreeLines.hpp"
#include "BridgeDetector.hpp"
#include "ClipperUtils.hpp"
#include "ClipperZUtils.hpp"
#include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "Feature/FuzzySkin/FuzzySkin.hpp"
@@ -296,13 +297,12 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
return out;
}
static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, const ClipperLib_Z::Paths& clip, ClipperLib_Z::ClipType clipType)
static ClipperZUtils::ZPaths clip_extrusion(const ClipperZUtils::ZPath& subject, const ClipperZUtils::ZPaths& clip, ClipType clipType)
{
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot,
const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
ClipperLib_Z::IntPoint start = e1bot;
ClipperLib_Z::IntPoint end = e1top;
auto zfill = [](const ClipperZUtils::ZPoint& e1bot, const ClipperZUtils::ZPoint& e1top, const ClipperZUtils::ZPoint& e2bot,
const ClipperZUtils::ZPoint& e2top, ClipperZUtils::ZPoint& pt) {
ClipperZUtils::ZPoint start = e1bot;
ClipperZUtils::ZPoint end = e1top;
if (start.z() <= 0 && end.z() <= 0) {
start = e2bot;
@@ -317,23 +317,15 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
double t = std::sqrt(dist_sqr / length_sqr);
pt.z() = start.z() + coord_t((end.z() - start.z()) * t);
});
};
clipper.AddPath(subject, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(clip, ClipperLib_Z::ptClip, true);
ClipperLib_Z::Paths clipped_paths;
{
ClipperLib_Z::PolyTree clipped_polytree;
clipper.Execute(clipType, clipped_polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(clipped_polytree), clipped_paths);
}
ClipperZUtils::ZPaths clipped_paths = ClipperZUtils::clip_zpaths(clipType, ClipperZUtils::ZPaths{ subject }, true, clip, zfill);
// Clipped path could contain vertices from the clip with a Z coordinate equal to zero.
// For those vertices, we must assign value based on the subject.
// This happens only in sporadic cases.
for (ClipperLib_Z::Path& path : clipped_paths)
for (ClipperLib_Z::IntPoint& c_pt : path)
for (ClipperZUtils::ZPath& path : clipped_paths)
for (ClipperZUtils::ZPoint& c_pt : path)
if (c_pt.z() == 0) {
// Now we must find the corresponding line on with this point is located and compute line width (Z coordinate).
if (subject.size() <= 2)
@@ -366,8 +358,8 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
}
assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool {
for (const ClipperLib_Z::Path& path : clipped_paths)
for (const ClipperLib_Z::IntPoint& pt : path)
for (const ClipperZUtils::ZPath& path : clipped_paths)
for (const ClipperZUtils::ZPoint& pt : path)
if (pt.z() <= 0)
return false;
return true;
@@ -376,7 +368,7 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
return clipped_paths;
}
static double clipper_z_path_length(const ClipperLib_Z::Path &path)
static double clipper_z_path_length(const ClipperZUtils::ZPath &path)
{
double len = 0.;
for (size_t i = 1; i < path.size(); ++ i)
@@ -413,7 +405,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
ExtrusionPaths paths;
// detect overhanging/bridging perimeters
if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) {
ClipperLib_Z::Path extrusion_path;
ClipperZUtils::ZPath extrusion_path;
extrusion_path.reserve(extrusion->size());
BoundingBox extrusion_path_bbox;
for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) {
@@ -421,7 +413,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y()));
}
ClipperLib_Z::Paths lower_slices_paths;
ClipperZUtils::ZPaths lower_slices_paths;
{
lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size());
Points clipped;
@@ -431,7 +423,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly.points, extrusion_path_bbox, clipped);
if (!clipped.empty()) {
lower_slices_paths.emplace_back();
ClipperLib_Z::Path &out = lower_slices_paths.back();
ClipperZUtils::ZPath &out = lower_slices_paths.back();
out.reserve(clipped.size());
for (const Point &pt : clipped)
out.emplace_back(pt.x(), pt.y(), 0);
@@ -440,7 +432,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
}
// get non-overhang paths by intersecting this loop with the grown lower slices
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ctIntersection), role,
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
// Always reverse extrusion if use fuzzy skin: https://github.com/OrcaSlicer/OrcaSlicer/pull/2413#issuecomment-1769735357
@@ -481,7 +473,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
// get overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter,
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ctDifference), erOverhangPerimeter,
perimeter_generator.overhang_flow);
// Reapply the nearest point search for starting point.
@@ -674,10 +666,10 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
};
// Pull the cut back by half a wall width: the clip severs the centerline, but the bead's rounded end
// extends half a width past its endpoint and would otherwise overlap the top fill.
ClipperLib_Z::Paths top_paths_z;
ClipperZUtils::ZPaths top_paths_z;
for (const Polygon &poly : to_polygons(offset_ex(top_region, float(perimeter_width) / 2.f))) {
top_paths_z.emplace_back();
ClipperLib_Z::Path &out = top_paths_z.back();
ClipperZUtils::ZPath &out = top_paths_z.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
@@ -695,21 +687,21 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
}
if (overlap == TopOverlap::Full)
continue; // the clip below would return nothing anyway
ClipperLib_Z::Path subject;
ClipperZUtils::ZPath subject;
subject.reserve(el.size());
for (const Arachne::ExtrusionJunction &j : el.junctions)
subject.emplace_back(j.p.x(), j.p.y(), j.w);
ClipperLib_Z::Paths pieces = clip_extrusion(subject, top_paths_z, ClipperLib_Z::ctDifference);
ClipperZUtils::ZPaths pieces = clip_extrusion(subject, top_paths_z, ctDifference);
// Clipper treats the subject as an open polyline, so it also cuts a closed loop at its (arbitrary)
// start vertex and may reverse pieces. Stitch pieces sharing an endpoint back together.
auto same_pt = [](const ClipperLib_Z::IntPoint &p, const ClipperLib_Z::IntPoint &q) {
auto same_pt = [](const ClipperZUtils::ZPoint &p, const ClipperZUtils::ZPoint &q) {
return std::abs(p.x() - q.x()) <= SCALED_EPSILON && std::abs(p.y() - q.y()) <= SCALED_EPSILON;
};
for (size_t i = 0; i < pieces.size(); ++ i) {
for (size_t j = i + 1; j < pieces.size();) {
ClipperLib_Z::Path &a = pieces[i];
ClipperLib_Z::Path &b = pieces[j];
ClipperZUtils::ZPath &a = pieces[i];
ClipperZUtils::ZPath &b = pieces[j];
if (same_pt(a.front(), b.front()) || same_pt(a.front(), b.back()))
std::reverse(a.begin(), a.end());
if (same_pt(a.back(), b.back()))
@@ -726,7 +718,7 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
// If the clip removed next to nothing, keep the loop untouched instead of slitting it open. The
// half-width pull-back above already costs about one width per crossing, hence two widths.
double kept_length = 0.;
for (const ClipperLib_Z::Path &path : pieces)
for (const ClipperZUtils::ZPath &path : pieces)
kept_length += clipper_z_path_length(path);
if (clipper_z_path_length(subject) - kept_length < 2. * double(perimeter_width)) {
append(kept_over_top, covered_by(el));
@@ -734,10 +726,10 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
continue;
}
for (const ClipperLib_Z::Path &path : pieces) {
for (const ClipperZUtils::ZPath &path : pieces) {
Arachne::ExtrusionLine clipped(el.inset_idx, el.is_odd);
clipped.junctions.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
for (const ClipperZUtils::ZPoint &pt : path)
clipped.junctions.emplace_back(Point(pt.x(), pt.y()), coord_t(pt.z()), el.inset_idx);
// Discard tiny leftovers that would print as zits.
if (clipped.size() >= 2 && clipped.getLength() >= perimeter_width)
@@ -1056,7 +1048,7 @@ ExtrusionPaths sort_extra_perimeters(const ExtrusionPaths& extra_perims, int ind
return filtered;
}
#define EXTRA_PERIMETER_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
#define EXTRA_PERIMETER_OFFSET_PARAMETERS jtSquare, 0.
// #define EXTRA_PERIM_DEBUG_FILES
// Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and
// Polygons filled by those clipped perimeters