diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index ce54a54c00..90928d3c1c 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -106,7 +106,7 @@ bool MultiPoint::remove_duplicate_points() Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) { Points result_pts; - double tolerance_sq = tolerance * tolerance; + auto tolerance_sq = int64_t(sqr(tolerance)); if (! pts.empty()) { const Point *anchor = &pts.front(); size_t anchor_idx = 0; @@ -120,14 +120,42 @@ Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) dpStack.reserve(pts.size()); dpStack.emplace_back(floater_idx); for (;;) { - double max_dist_sq = 0.0; - size_t furthest_idx = anchor_idx; + int64_t max_dist_sq = 0; + size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater); - if (dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; + { + const Point a = *anchor; + const Point f = *floater; + const Vec2i64 v = (f - a).cast(); + const int64_t l2 = v.squaredNorm(); + // Make up for rounding when converting from int64_t to double. Double mantissa is just 52 bits. + if (l2 < (1 << 14)) { + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) + if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } else { + const double dl2 = double(l2); + const Vec2d dv = v.cast(); + for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { + const Point p = pts[i]; + const Vec2i64 va = (p - a).template cast(); + const int64_t t = va.dot(v); + int64_t dist_sq; + if (t <= 0) { + dist_sq = va.squaredNorm(); + } else if (t >= l2) { + dist_sq = (p - f).cast().squaredNorm(); + } else { + const Vec2i64 w = ((double(t) / dl2) * dv).cast(); + dist_sq = (w - va).squaredNorm(); + } + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } } } // remove point if less than tolerance