From d370edb5b287359df93d04dba451b60a1ca9a642 Mon Sep 17 00:00:00 2001 From: Rad Date: Sun, 19 Apr 2026 17:50:18 +0200 Subject: [PATCH] bugfix in progress --- src/libslic3r/PerimeterGenerator.cpp | 13 ++- src/libslic3r/VariableWidth.cpp | 141 ++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index e81dca93d4..aa74e8c4bd 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -30,6 +30,14 @@ namespace Slic3r { using namespace Slic3r::Feature::FuzzySkin; +static bool is_gap_fill_allowed_for_surface(const PrintObjectConfig* config, const SurfaceType surface_type) +{ + if (config == nullptr || config->gap_fill_target.value == gftNowhere) + return false; + + return surface_type != stInternalSolid || config->gap_fill_target.value == gftEverywhere; +} + static void append_collapsed_infill_gap_fill(const ExPolygons &areas, const Flow &flow, const double simplify_resolution, @@ -1706,7 +1714,7 @@ void PerimeterGenerator::process_classic() not_filled_exp, float(-inset - min_perimeter_infill_spacing / 2.), float(min_perimeter_infill_spacing / 2.)); - if (infill_exp.empty() && has_gap_fill) { + if (infill_exp.empty() && has_gap_fill && is_gap_fill_allowed_for_surface(object_config, surface.surface_type)) { ExPolygons gap_fill_source = not_filled_exp.empty() ? collapsed_gap_fill_source : not_filled_exp; if (gap_fill_source.empty()) gap_fill_source = ExPolygons { surface.expolygon }; @@ -2581,7 +2589,8 @@ void PerimeterGenerator::process_arachne() not_filled_exp, float(-min_perimeter_infill_spacing / 2.), float(inset + min_perimeter_infill_spacing / 2.)); - if (infill_exp.empty() && this->config->gap_infill_speed.value > 0) { + if (infill_exp.empty() && this->config->gap_infill_speed.value > 0 && + is_gap_fill_allowed_for_surface(object_config, surface.surface_type)) { ExPolygons gap_fill_source = not_filled_exp.empty() ? collapsed_gap_fill_source : not_filled_exp; if (gap_fill_source.empty()) gap_fill_source = ExPolygons { surface.expolygon }; diff --git a/src/libslic3r/VariableWidth.cpp b/src/libslic3r/VariableWidth.cpp index e01050e4a4..8695e4c55e 100644 --- a/src/libslic3r/VariableWidth.cpp +++ b/src/libslic3r/VariableWidth.cpp @@ -1,7 +1,119 @@ #include "VariableWidth.hpp" +#include "BoundingBox.hpp" + namespace Slic3r { +static Points closed_loop_points(const ThickPolyline& thick_polyline) +{ + Points points = thick_polyline.points; + if (points.size() > 1 && points.front() == points.back()) + points.pop_back(); + return points; +} + +static bool points_match_cyclic(const Points& lhs, const Points& rhs, const coord_t point_tolerance, const bool reversed) +{ + if (lhs.size() != rhs.size() || lhs.empty()) + return false; + + const double tolerance_sq = double(point_tolerance) * double(point_tolerance); + for (size_t start = 0; start < rhs.size(); ++start) { + if ((lhs.front() - rhs[start]).cast().squaredNorm() > tolerance_sq) + continue; + + bool matches = true; + for (size_t i = 0; i < lhs.size(); ++i) { + const size_t rhs_idx = reversed ? (start + rhs.size() - i) % rhs.size() : (start + i) % rhs.size(); + if ((lhs[i] - rhs[rhs_idx]).cast().squaredNorm() > tolerance_sq) { + matches = false; + break; + } + } + + if (matches) + return true; + } + + return false; +} + +static bool are_near_duplicate_closed_gap_fill_loops(const ThickPolyline& lhs, const ThickPolyline& rhs) +{ + if (!lhs.is_closed() || !rhs.is_closed()) + return false; + + const Points lhs_points = closed_loop_points(lhs); + const Points rhs_points = closed_loop_points(rhs); + if (lhs_points.size() < 3 || lhs_points.size() != rhs_points.size()) + return false; + + const coord_t bbox_tolerance = scale_(0.05); + const coord_t point_tolerance = scale_(0.05); + const double length_tolerance = double(scale_(0.20)); + + const BoundingBox lhs_bbox(lhs_points); + const BoundingBox rhs_bbox(rhs_points); + if (!lhs_bbox.defined || !rhs_bbox.defined) + return false; + + if (std::abs(lhs_bbox.min.x() - rhs_bbox.min.x()) > bbox_tolerance || + std::abs(lhs_bbox.min.y() - rhs_bbox.min.y()) > bbox_tolerance || + std::abs(lhs_bbox.max.x() - rhs_bbox.max.x()) > bbox_tolerance || + std::abs(lhs_bbox.max.y() - rhs_bbox.max.y()) > bbox_tolerance) + return false; + + if (std::abs(length(lhs_points) - length(rhs_points)) > length_tolerance) + return false; + + const double area_tolerance = double(scale_(0.05)) * std::max(length(lhs_points), length(rhs_points)); + if (std::abs(std::abs(area(lhs_points)) - std::abs(area(rhs_points))) > area_tolerance) + return false; + + return points_match_cyclic(lhs_points, rhs_points, point_tolerance, false) || + points_match_cyclic(lhs_points, rhs_points, point_tolerance, true); +} + +static ExtrusionPaths closed_gap_fill_loop_to_extrusion_paths(const ThickPolyline& thick_polyline, ExtrusionRole role, const Flow& flow) +{ + ExtrusionPaths paths; + if (!thick_polyline.is_closed()) + return paths; + + ThickLines lines = thick_polyline.thicklines(); + if (lines.empty()) + return paths; + + double total_length = 0.0; + double weighted_width_sum = 0.0; + for (const ThickLine& line : lines) { + const coordf_t line_len = line.length(); + if (line_len < SCALED_EPSILON) + continue; + + total_length += line_len; + weighted_width_sum += line_len * 0.5 * (line.a_width + line.b_width); + } + + if (total_length <= SCALED_EPSILON) + return paths; + + ExtrusionPath path(role); + path.polyline.points = thick_polyline.points; + if (!path.polyline.is_valid()) + return paths; + + const double average_width = weighted_width_sum / total_length; + const Flow new_flow = (role == erOverhangPerimeter && flow.bridge()) ? + flow : + flow.with_width(unscale(average_width) + flow.height() * float(1. - 0.25 * PI)); + path.mm3_per_mm = new_flow.mm3_per_mm(); + path.width = new_flow.width(); + path.height = new_flow.height(); + paths.emplace_back(std::move(path)); + return paths; +} + ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyline, ExtrusionRole role, const Flow& flow, const float tolerance, const float merge_tolerance) { ExtrusionMultiPath multi_path; @@ -219,8 +331,33 @@ void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const F // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance. const float tolerance = float(scale_(0.05)); - for (const ThickPolyline& p : polylines) { - ExtrusionPaths paths = thick_polyline_to_extrusion_paths_2(p, role, flow, tolerance); + const ThickPolylines* source_polylines = &polylines; + ThickPolylines deduplicated_gap_fill_loops; + + if (role == erGapFill && polylines.size() > 1) { + deduplicated_gap_fill_loops.reserve(polylines.size()); + for (const ThickPolyline& polyline : polylines) { + bool is_duplicate = false; + for (const ThickPolyline& kept : deduplicated_gap_fill_loops) { + if (are_near_duplicate_closed_gap_fill_loops(polyline, kept)) { + is_duplicate = true; + break; + } + } + + if (!is_duplicate) + deduplicated_gap_fill_loops.emplace_back(polyline); + } + + source_polylines = &deduplicated_gap_fill_loops; + } + + for (const ThickPolyline& p : *source_polylines) { + ExtrusionPaths paths; + if (role == erGapFill && p.is_closed()) + paths = closed_gap_fill_loop_to_extrusion_paths(p, role, flow); + if (paths.empty()) + paths = thick_polyline_to_extrusion_paths_2(p, role, flow, tolerance); // Append paths to collection. if (!paths.empty()) { if (paths.front().first_point() == paths.back().last_point())