diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 0a138834a7..1f1f1289e5 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -2256,13 +2256,45 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim // ORCA: // Inner Outer Inner wall ordering mode perimeter order optimisation functions + +// Whether two Arachne lines touch: somewhere the gap between their centrelines is no more than the +// touching distance there. Each junction of one line is measured against the segments of the other, +// both ways, and the search stops at the first spot that touches. +// Arachne varies line width to fill the region (e.g. the odd centre line of a narrow wall is wider +// than nominal), so the touching distance is half the combined width at the closest points, not the +// nominal spacing. Widths are taken locally so a line widened in one place (a wedge tip, a wall +// transition) does not count as touching where it passes close to other perimeters. min_threshold keeps +// the nominal spacing threshold as the lower bound. +static bool arachne_lines_touch(const Arachne::ExtrusionLine &a, const Arachne::ExtrusionLine &b, double min_threshold) +{ + auto one_way = [min_threshold](const Arachne::ExtrusionLine &from, const Arachne::ExtrusionLine &to) { + for (const Arachne::ExtrusionJunction &j : from.junctions) { + const Vec2d p = j.p.cast(); + for (size_t k = 0; k + 1 < to.junctions.size(); ++k) { + const Arachne::ExtrusionJunction &j0 = to.junctions[k]; + const Arachne::ExtrusionJunction &j1 = to.junctions[k + 1]; + const Vec2d s0 = j0.p.cast(); + const Vec2d seg = j1.p.cast() - s0; + const double l2 = seg.squaredNorm(); + const double t = l2 > 0. ? std::clamp((p - s0).dot(seg) / l2, 0., 1.) : 0.; + const double w = double(j0.w) + t * double(j1.w - j0.w); // width of `to` at the closest point + const double touch_distance = std::max(min_threshold, 0.5 * (double(j.w) + w)); + if ((s0 + t * seg - p).norm() <= touch_distance) + return true; + } + } + return false; + }; + return one_way(a, b) || one_way(b, a); +} + /** * @brief Finds all perimeters touching a given set of reference lines, given as indexes. * * @param entities The list of PerimeterGeneratorArachneExtrusion entities. * @param referenceIndices A set of indices representing the reference points. - * @param threshold_external The distance threshold to consider for proximity for a reference perimeter with inset index 0 - * @param threshold_internal The distance threshold to consider for proximity for a reference perimeter with inset index 1+ + * @param threshold_external The minimum touching distance for a reference perimeter with inset index 0 + * @param threshold_internal The minimum touching distance for a reference perimeter with inset index 1+ * @param considered_inset_idx What perimeter inset index are we searching for (eg. if we are searching for first internal perimeters proximate to the current reference perimeter, this value should be set to 1 etc). * @return std::vector A vector of indices representing the touching perimeters. */ @@ -2271,7 +2303,6 @@ std::vector findAllTouchingPerimeters(const std::vector 0) continue; @@ -2282,15 +2313,9 @@ std::vector findAllTouchingPerimeters(const std::vectorinset_idx == 0) - threshold = threshold_external; - else - threshold = threshold_internal; - if (distance <= threshold) { + // Add to touchingIndices if the lines touch. + const double threshold = double(referenceEntity.extrusion->inset_idx == 0 ? threshold_external : threshold_internal); + if (arachne_lines_touch(*referenceEntity.extrusion, *entity.extrusion, threshold)) { touchingIndices.insert(i); } }