diff --git a/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp b/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp index 494b7b0b6..c8a84c401 100644 --- a/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp +++ b/src/libslic3r/Arachne/BeadingStrategy/DistributedBeadingStrategy.cpp @@ -21,7 +21,7 @@ DistributedBeadingStrategy::DistributedBeadingStrategy(const coord_t optimal_wid name = "DistributedBeadingStrategy"; } -DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t thickness, coord_t bead_count) const +DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(const coord_t thickness, const coord_t bead_count) const { Beading ret; @@ -40,18 +40,24 @@ DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) weights[bead_idx] = getWeight(bead_idx); - const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f); + const float total_weight = std::accumulate(weights.cbegin(), weights.cend(), 0.f); + coord_t accumulated_width = 0; for (coord_t bead_idx = 0; bead_idx < bead_count; bead_idx++) { - const float weight_fraction = weights[bead_idx] / total_weight; + const float weight_fraction = weights[bead_idx] / total_weight; const coord_t splitup_left_over_weight = to_be_divided * weight_fraction; - const coord_t width = optimal_width + splitup_left_over_weight; + const coord_t width = (bead_idx == bead_count - 1) ? thickness - accumulated_width : optimal_width + splitup_left_over_weight; + + // Be aware that toolpath_locations is computed by dividing the width by 2, so toolpath_locations + // could be off by 1 because of rounding errors. if (bead_idx == 0) ret.toolpath_locations.emplace_back(width / 2); else ret.toolpath_locations.emplace_back(ret.toolpath_locations.back() + (ret.bead_widths.back() + width) / 2); ret.bead_widths.emplace_back(width); + accumulated_width += width; } ret.left_over = 0; + assert((accumulated_width + ret.left_over) == thickness); } else if (bead_count == 2) { const coord_t outer_width = thickness / 2; ret.bead_widths.emplace_back(outer_width); @@ -68,6 +74,13 @@ DistributedBeadingStrategy::Beading DistributedBeadingStrategy::compute(coord_t ret.left_over = thickness; } + assert(([&ret = std::as_const(ret), thickness]() -> bool { + coord_t total_bead_width = 0; + for (const coord_t &bead_width : ret.bead_widths) + total_bead_width += bead_width; + return (total_bead_width + ret.left_over) == thickness; + }())); + return ret; } diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index 34d6d058d..fbd3379d5 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -525,7 +525,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) #endif #ifdef ARACHNE_DEBUG - assert(is_voronoi_diagram_planar_intersection(voronoi_diagram)); + assert(Geometry::VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(voronoi_diagram)); #endif // Try to detect cases when some Voronoi vertex is missing and when @@ -1911,7 +1911,10 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t& for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--) { coord_t bead_R = beading->toolpath_locations[junction_idx]; - if (bead_R <= start_R) + // toolpath_locations computed inside DistributedBeadingStrategy be off by 1 because of rounding errors. + // In GH issue #8472, these roundings errors caused missing the middle extrusion. + // Adding some epsilon should help resolve those cases. + if (bead_R <= start_R + scaled(0.005)) { // Junction coinciding with start node is used in this function call break; } diff --git a/tests/libslic3r/test_arachne.cpp b/tests/libslic3r/test_arachne.cpp index 53c31169a..f2c1f7c4b 100644 --- a/tests/libslic3r/test_arachne.cpp +++ b/tests/libslic3r/test_arachne.cpp @@ -73,3 +73,32 @@ TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") { } } +// This test case was distilled from GitHub issue #8472. +// Where for wall_distribution_count == 3 sometime middle perimeter was missing. +TEST_CASE("Arachne - Missing perimeter - #8472", "[ArachneMissingPerimeter8472]") { + Polygon poly = { + Point(-9000000, 8054793), + Point( 7000000, 8054793), + Point( 7000000, 10211874), + Point(-8700000, 10211874), + Point(-9000000, 9824444) + }; + + Polygons polygons = {poly}; + coord_t spacing = 437079; + coord_t inset_count = 3; + + PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); + print_object_config.wall_distribution_count.setInt(3); + + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, print_object_config, PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + draw_extrusion(debug_out_path("arachne-missing-perimeter-8472.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif + + REQUIRE(perimeters.size() == 3); +} +