From 78b00bb9cb8a07055951409bab36e1adc0bb3b22 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 13 Apr 2023 16:09:29 +0200 Subject: [PATCH] Tests for duplicate points in Polygons / ExPolygons were reworked to use ankerl::unordered_dense hash map. Now the tests are roughly 1/4 faster than before. --- src/libslic3r/ExPolygon.cpp | 32 +++++++++++++++++++++++++------- src/libslic3r/Polygon.cpp | 28 ++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index dc991e46d4..b6d12a6023 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -10,6 +10,8 @@ #include #include +#include + namespace Slic3r { void ExPolygon::scale(double factor) @@ -416,20 +418,36 @@ bool has_duplicate_points(const ExPolygons &expolys) { #if 1 // Check globally. - size_t cnt = 0; - for (const ExPolygon &expoly : expolys) { - cnt += expoly.contour.points.size(); - for (const Polygon &hole : expoly.holes) - cnt += hole.points.size(); - } +#if 0 + // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster. std::vector allpts; - allpts.reserve(cnt); + allpts.reserve(count_points(expolys)); for (const ExPolygon &expoly : expolys) { allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end()); for (const Polygon &hole : expoly.holes) allpts.insert(allpts.end(), hole.points.begin(), hole.points.end()); } return has_duplicate_points(std::move(allpts)); +#else + // Detect duplicates by inserting into an ankerl::unordered_dense hash set, which is is around 1/4 faster than qsort. + struct PointHash { + uint64_t operator()(const Point &p) const noexcept { + uint64_t h; + static_assert(sizeof(h) == sizeof(p)); + memcpy(&h, &p, sizeof(p)); + return ankerl::unordered_dense::detail::wyhash::hash(h); + } + }; + ankerl::unordered_dense::set allpts; + allpts.reserve(count_points(expolys)); + for (const ExPolygon &expoly : expolys) + for (size_t icontour = 0; icontour < expoly.num_contours(); ++ icontour) + for (const Point &pt : expoly.contour_or_hole(icontour).points) + if (! allpts.insert(pt).second) + // Duplicate point was discovered. + return true; + return false; +#endif #else // Check per contour. for (const ExPolygon &expoly : expolys) diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index d342f3d917..3be36984c5 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -4,6 +4,8 @@ #include "Polygon.hpp" #include "Polyline.hpp" +#include + namespace Slic3r { double Polygon::length() const @@ -400,14 +402,32 @@ bool has_duplicate_points(const Polygons &polys) { #if 1 // Check globally. - size_t cnt = 0; - for (const Polygon &poly : polys) - cnt += poly.points.size(); +#if 0 + // Detect duplicates by sorting with quicksort. It is quite fast, but ankerl::unordered_dense is around 1/4 faster. std::vector allpts; - allpts.reserve(cnt); + allpts.reserve(count_points(polys)); for (const Polygon &poly : polys) allpts.insert(allpts.end(), poly.points.begin(), poly.points.end()); return has_duplicate_points(std::move(allpts)); +#else + // Detect duplicates by inserting into an ankerl::unordered_dense hash set, which is is around 1/4 faster than qsort. + struct PointHash { + uint64_t operator()(const Point &p) const noexcept { + uint64_t h; + static_assert(sizeof(h) == sizeof(p)); + memcpy(&h, &p, sizeof(p)); + return ankerl::unordered_dense::detail::wyhash::hash(h); + } + }; + ankerl::unordered_dense::set allpts; + allpts.reserve(count_points(polys)); + for (const Polygon &poly : polys) + for (const Point &pt : poly.points) + if (! allpts.insert(pt).second) + // Duplicate point was discovered. + return true; + return false; +#endif #else // Check per contour. for (const Polygon &poly : polys)