From 06cf42cbbdd9151e405f05f51085bc59d7e1015b Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 5 Aug 2026 15:57:22 +0200 Subject: [PATCH 1/3] Re-fix random dots without introducing microsegments. Originally, an issue (see #2089 / C-9399) was fixed, where we needed to re-insert some ignored local maxima. This fix was intended to be applied only for very small areas. Large areas didn't need to be corrected for, certainly not ones large enough to be a problem w.r.t non-convexness. (And non-connected shapes won't be treated as a single area w.r.t. SkeletalTrapezoidation.) -- We found, while doing some other work, that this could cause points to appear in the middle of nowhere (see commets in #2346 / C-13250). This was fixed by taking into account that averaged points should only be merged when close togehter. -- The real problem was however that the fix was also applied to large areas (otherwise the middle of nowhere behaviour wouldn't be possible at all). If we restrict the fix to only be for cases where the total length of the covered lines is very small, we get the originally inteded behaviour of the hack (and this is not a problem, because there can't be multiple areas within a single trapezoidation other than holes, and areas _otherwise_ large enough to contain multiple clearly separate local maxima are not the intended target for the original and now curent fix anyway). CURA-13287 --- src/arachne/SkeletalTrapezoidation.cpp | 32 ++++++++++---------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/arachne/SkeletalTrapezoidation.cpp b/src/arachne/SkeletalTrapezoidation.cpp index 601b3bf630..fd43d89aab 100644 --- a/src/arachne/SkeletalTrapezoidation.cpp +++ b/src/arachne/SkeletalTrapezoidation.cpp @@ -2212,11 +2212,16 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads() Point2LL p_; coord_t width_; size_t acc_; + coord_t length_; + + // NOTE: Empty constructor; `acc_` is set to 0, so that it can be used as a neutral element in the `+=` operator. + LocalMaximaPoint() : p_(0, 0), width_(0), acc_(0), length_(0) {} LocalMaximaPoint(const Point2LL& p, coord_t width) : p_(p) , width_(width) , acc_(1) + , length_(0) { } @@ -2225,19 +2230,20 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads() p_ = (p_ * acc_ + other.p_ * other.acc_) / (acc_ + other.acc_); width_ = (width_ * acc_ + other.width_ * other.acc_) / (acc_ + other.acc_); acc_ += other.acc_; + length_ += other.length_ + (acc_ >= 1 && other.acc_ >= 1) ? vSize(p_ - other.p_) : 0L; } }; - std::vector local_maxima_points; + LocalMaximaPoint combined_local_maxima_point; for (const auto& node : graph_.nodes_) { - if (! node.data_.hasBeading()) + if (! (node.data_.hasBeading() && node.isLocalMaximum(true))) { continue; } const Beading& beading = node.data_.getBeading()->beading_; - if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true)) + if (beading.bead_widths.size() % 2 == 1) { const size_t inset_index = beading.bead_widths.size() / 2; const coord_t width = beading.bead_widths[inset_index]; @@ -2247,28 +2253,14 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads() } else { - const auto it = ranges::find_if( - local_maxima_points, - [&](const LocalMaximaPoint& local_maxima_point) - { - return vSize2(local_maxima_point.p_ - node.p_) < 10 * 10; - }); - - if (it != local_maxima_points.end()) - { - *it += (LocalMaximaPoint(node.p_, width)); - } - else - { - local_maxima_points.emplace_back(node.p_, width); - } + combined_local_maxima_point += LocalMaximaPoint(node.p_, width); } } } - for (const auto& local_maxima_point : local_maxima_points) + if (combined_local_maxima_point.length_ < (combined_local_maxima_point.width_ * 2L)) { - addCircleToToolpath(local_maxima_point.p_, local_maxima_point.width_, 0); + addCircleToToolpath(combined_local_maxima_point.p_, combined_local_maxima_point.width_, 0); } } // From b38dc4d02f3deffbdff33af134c55a434486e78e Mon Sep 17 00:00:00 2001 From: rburema <41987080+rburema@users.noreply.github.com> Date: Wed, 5 Aug 2026 14:02:03 +0000 Subject: [PATCH 2/3] Apply clang-format --- src/arachne/SkeletalTrapezoidation.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arachne/SkeletalTrapezoidation.cpp b/src/arachne/SkeletalTrapezoidation.cpp index fd43d89aab..47fb2200b4 100644 --- a/src/arachne/SkeletalTrapezoidation.cpp +++ b/src/arachne/SkeletalTrapezoidation.cpp @@ -2215,7 +2215,13 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads() coord_t length_; // NOTE: Empty constructor; `acc_` is set to 0, so that it can be used as a neutral element in the `+=` operator. - LocalMaximaPoint() : p_(0, 0), width_(0), acc_(0), length_(0) {} + LocalMaximaPoint() + : p_(0, 0) + , width_(0) + , acc_(0) + , length_(0) + { + } LocalMaximaPoint(const Point2LL& p, coord_t width) : p_(p) From bb861bca1db215629e30b9b272c50f538fb7b2b9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 5 Aug 2026 16:12:40 +0200 Subject: [PATCH 3/3] Don't add 'empty' (combined local maxima) points. I actually wanted to use an optional, but that turned out to work a bit ugly with the += operator. But then I just deleted the 'does this actually exist' check, instead of replacing it with this. Fixed. part of CURA-13287 --- src/arachne/SkeletalTrapezoidation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arachne/SkeletalTrapezoidation.cpp b/src/arachne/SkeletalTrapezoidation.cpp index 47fb2200b4..bc3684363f 100644 --- a/src/arachne/SkeletalTrapezoidation.cpp +++ b/src/arachne/SkeletalTrapezoidation.cpp @@ -2264,7 +2264,7 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads() } } - if (combined_local_maxima_point.length_ < (combined_local_maxima_point.width_ * 2L)) + if (combined_local_maxima_point.acc_ > 0 && combined_local_maxima_point.length_ < (combined_local_maxima_point.width_ * 2L)) { addCircleToToolpath(combined_local_maxima_point.p_, combined_local_maxima_point.width_, 0); }