From f16feff2a4951bc38fc75b49bb859d9f38d2f553 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Thu, 9 Nov 2023 22:21:44 +0800 Subject: [PATCH] Fixes scars on thing walls when "avoid crossing wall" is turned on (#2659) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Travel very close to the inner holes, could cross external perimeter when the avoid crossing perimeters was enabled. (cherry picked from commit prusa3d/PrusaSlicer@6c51e5148c0fca15c869d344e081aad249b0244f) * Modified variable offset in the avoid crossing perimeters to not cause scars on thin objects (#7699). Previously, the minimum contour width was chosen too conservative and, on some thin objects, only allowed minimal (or non) offset. This could result in travels being planned along the outer perimeter. Now, the minimum contour width is chosen much smaller at the start and tested if the variable offset wasn't failed (the outer contour broke up into more parts, more or fewer holes, etc.). If any problem is detected, the variable offset is recalculated with a larger minimum contour width. (cherry picked from commit prusa3d/PrusaSlicer@dc00f0bf983902a6cb81503afa78be4a0ccafb71) --------- Co-authored-by: Lukáš Hejl --- .../GCode/AvoidCrossingPerimeters.cpp | 261 +++++++++++------- .../GCode/AvoidCrossingPerimeters.hpp | 9 +- 2 files changed, 168 insertions(+), 102 deletions(-) diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 4d9f3dcf1b..0598db3b17 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "../Layer.hpp" #include "../GCode.hpp" #include "../EdgeGrid.hpp" @@ -13,6 +17,8 @@ #include #include +//#define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT + namespace Slic3r { struct TravelPoint @@ -20,6 +26,8 @@ struct TravelPoint Point point; // Index of the polygon containing this point. A negative value indicates that the point is not on any border. int border_idx; + // simplify_travel() doesn't remove this point. + bool do_not_remove = false; }; struct Intersection @@ -32,6 +40,8 @@ struct Intersection Point point; // Distance from the first point in the corresponding boundary float distance; + // simplify_travel() doesn't remove this point. + bool do_not_remove = false; }; struct ClosestLine @@ -207,8 +217,8 @@ static std::vector extend_for_closest_lines(const std::vector new_intersections; - new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}); - new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}); + new_intersections.push_back({cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true}); + new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true}); return new_intersections; } } @@ -259,7 +269,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) { // If there is any ClosestLine around the start point closer to the Intersection, then replace this Intersection with ClosestLine. const ClosestLine &cl_start = start_lines[cl_start_idx]; - new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}; + new_intersections.front() = {cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true}; } else { // Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the // vector of intersections. This allows in some cases when it is more than one around ClosestLine start point chose that one which @@ -267,7 +277,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) ? start_lines[start_closest_lines_idx] : start_lines.front(); - new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start)}); + new_intersections.insert(new_intersections.begin(),{cl_start.border_idx, cl_start.line_idx, cl_start.point, compute_distance(cl_start), true}); } } @@ -276,7 +286,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) { // If there is any ClosestLine around the end point closer to the Intersection, then replace this Intersection with ClosestLine. const ClosestLine &cl_end = end_lines[cl_end_idx]; - new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}; + new_intersections.back() = {cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true}; } else { // Check if there is any ClosestLine with the same boundary_idx as any Intersection. If this ClosestLine exists, then add it to the // vector of intersections. This allows in some cases when it is more than one around ClosestLine end point chose that one which @@ -284,7 +294,7 @@ static std::vector extend_for_closest_lines(const std::vector::max()) ? end_lines[end_closest_lines_idx] : end_lines.front(); - new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end)}); + new_intersections.push_back({cl_end.border_idx, cl_end.line_idx, cl_end.point, compute_distance(cl_end), true}); } } return new_intersections; @@ -350,8 +360,6 @@ static Polyline to_polyline(const std::vector &travel) return result; } -// #define AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT - #ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT static void export_travel_to_svg(const Polygons &boundary, const Line &original_travel, @@ -359,16 +367,17 @@ static void export_travel_to_svg(const Polygons &boundary, const std::vector &intersections, const std::string &path) { - BoundingBox bbox = get_extents(boundary); + coordf_t stroke_width = scale_(0.05); + BoundingBox bbox = get_extents(boundary); ::Slic3r::SVG svg(path, bbox); - svg.draw_outline(boundary, "green"); - svg.draw(original_travel, "blue"); - svg.draw(result_travel, "red"); - svg.draw(original_travel.a, "black"); - svg.draw(original_travel.b, "grey"); + svg.draw_outline(boundary, "green", stroke_width); + svg.draw(original_travel, "blue", stroke_width); + svg.draw(result_travel, "red", stroke_width); + svg.draw(original_travel.a, "black", stroke_width); + svg.draw(original_travel.b, "grey", stroke_width); for (const Intersection &intersection : intersections) - svg.draw(intersection.point, "lightseagreen"); + svg.draw(intersection.point, "lightseagreen", stroke_width); } static void export_travel_to_svg(const Polygons &boundary, @@ -433,21 +442,30 @@ static std::vector simplify_travel(const AvoidCrossingPerimeters::B visitor.pt_current = ¤t_point; - for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) { - if (travel[point_idx_2].point == current_point) { - next = travel[point_idx_2]; - point_idx = point_idx_2; - continue; - } + if (!next.do_not_remove) + for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) { + // Workaround for some issue in MSVC 19.29.30037 32-bit compiler. +#if defined(_WIN32) && !defined(_WIN64) + if (bool volatile do_not_remove = travel[point_idx_2].do_not_remove; do_not_remove) + break; +#else + if (travel[point_idx_2].do_not_remove) + break; +#endif + if (travel[point_idx_2].point == current_point) { + next = travel[point_idx_2]; + point_idx = point_idx_2; + continue; + } - visitor.pt_next = &travel[point_idx_2].point; - boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); - // Check if deleting point causes crossing a boundary - if (!visitor.intersect) { - next = travel[point_idx_2]; - point_idx = point_idx_2; + visitor.pt_next = &travel[point_idx_2].point; + boundary.grid.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); + // Check if deleting point causes crossing a boundary + if (!visitor.intersect) { + next = travel[point_idx_2]; + point_idx = point_idx_2; + } } - } simplified_path.emplace_back(next); } @@ -473,7 +491,7 @@ static float get_perimeter_spacing(const Layer &layer) size_t regions_count = 0; float perimeter_spacing = 0.f; for (const LayerRegion *layer_region : layer.regions()) - if (layer_region != nullptr && !layer_region->slices.empty()) { + if (layer_region != nullptr && ! layer_region->slices.empty()) { perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing(); ++regions_count; } @@ -494,7 +512,7 @@ static float get_perimeter_spacing_external(const Layer &layer) for (const PrintObject *object : layer.object()->print()->objects()) if (const Layer *l = object->get_layer_at_printz(layer.print_z, EPSILON); l) for (const LayerRegion *layer_region : l->regions()) - if (layer_region != nullptr && !layer_region->slices.empty()) { + if (layer_region != nullptr && ! layer_region->slices.empty()) { perimeter_spacing += layer_region->flow(frPerimeter).scaled_spacing(); ++ regions_count; } @@ -507,6 +525,25 @@ static float get_perimeter_spacing_external(const Layer &layer) return perimeter_spacing; } +// Returns average perimeter width calculated from all LayerRegion within the layer. +static float get_external_perimeter_width(const Layer &layer) +{ + size_t regions_count = 0; + float perimeter_width = 0.f; + for (const LayerRegion *layer_region : layer.regions()) + if (layer_region != nullptr && ! layer_region->slices.empty()) { + perimeter_width += float(layer_region->flow(frExternalPerimeter).scaled_width()); + ++regions_count; + } + + assert(perimeter_width >= 0.f); + if (regions_count != 0) + perimeter_width /= float(regions_count); + else + perimeter_width = get_default_perimeter_spacing(*layer.object()); + return perimeter_width; +} + // Called by avoid_perimeters() and by simplify_travel_heuristics(). static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &boundary, const Point &start, @@ -566,7 +603,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo // Offset of the polygon's point using get_middle_point_offset is used to simplify the calculation of intersection between the // boundary and the travel. The appended point is translated in the direction of inward normal. This translation ensures that the // appended point will be inside the polygon and not on the polygon border. - result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx)}); + result.push_back({get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, coord_t(SCALED_EPSILON)), int(intersection_first.border_idx), intersection_first.do_not_remove}); // Check if intersection line also exit the boundary polygon if (it_second_r != it_last_item) { @@ -590,7 +627,7 @@ static size_t avoid_perimeters_inner(const AvoidCrossingPerimeters::Boundary &bo // Append the farthest intersection into the path left_idx = intersection_second.line_idx; right_idx = (intersection_second.line_idx >= (boundaries[intersection_second.border_idx].points.size() - 1)) ? 0 : (intersection_second.line_idx + 1); - result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx)}); + result.push_back({get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, coord_t(SCALED_EPSILON)), int(intersection_second.border_idx), intersection_second.do_not_remove}); // Skip intersections in between it_first = it_second; } @@ -645,22 +682,22 @@ static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary // Check if anyone of ExPolygons contains whole travel. // called by need_wipe() and AvoidCrossingPerimeters::travel_to() // FIXME Lukas H.: Maybe similar approach could also be used for ExPolygon::contains() -static bool any_expolygon_contains(const ExPolygons &ex_polygons, - const std::vector &ex_polygons_bboxes, - const EdgeGrid::Grid &grid_lslice, +static bool any_expolygon_contains(const ExPolygons &lslices_offset, + const std::vector &lslices_offset_bboxes, + const EdgeGrid::Grid &grid_lslices_offset, const Line &travel) { - assert(ex_polygons.size() == ex_polygons_bboxes.size()); - if(!grid_lslice.bbox().contains(travel.a) || !grid_lslice.bbox().contains(travel.b)) + assert(lslices_offset.size() == lslices_offset_bboxes.size()); + if(!grid_lslices_offset.bbox().contains(travel.a) || !grid_lslices_offset.bbox().contains(travel.b)) return false; - FirstIntersectionVisitor visitor(grid_lslice); + FirstIntersectionVisitor visitor(grid_lslices_offset); visitor.pt_current = &travel.a; visitor.pt_next = &travel.b; - grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); + grid_lslices_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); if (!visitor.intersect) { - for (const ExPolygon &ex_polygon : ex_polygons) { - const BoundingBox &bbox = ex_polygons_bboxes[&ex_polygon - &ex_polygons.front()]; + for (const ExPolygon &ex_polygon : lslices_offset) { + const BoundingBox &bbox = lslices_offset_bboxes[&ex_polygon - &lslices_offset.front()]; if (bbox.contains(travel.a) && bbox.contains(travel.b) && ex_polygon.contains(travel.a)) return true; } @@ -670,18 +707,18 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, // Check if anyone of ExPolygons contains whole travel. // called by need_wipe() -static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice, const Polyline &travel) +static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vector &ex_polygons_bboxes, const EdgeGrid::Grid &grid_lslice_offset, const Polyline &travel) { assert(ex_polygons.size() == ex_polygons_bboxes.size()); - if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice](const Point &point) { return !grid_lslice.bbox().contains(point); })) + if(std::any_of(travel.points.begin(), travel.points.end(), [&grid_lslice_offset](const Point &point) { return !grid_lslice_offset.bbox().contains(point); })) return false; - FirstIntersectionVisitor visitor(grid_lslice); + FirstIntersectionVisitor visitor(grid_lslice_offset); bool any_intersection = false; for (size_t line_idx = 1; line_idx < travel.size(); ++line_idx) { visitor.pt_current = &travel.points[line_idx - 1]; visitor.pt_next = &travel.points[line_idx]; - grid_lslice.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); + grid_lslice_offset.visit_cells_intersecting_line(*visitor.pt_current, *visitor.pt_next, visitor); any_intersection = visitor.intersect; if (any_intersection) break; } @@ -698,13 +735,13 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec } static bool need_wipe(const GCode &gcodegen, - const EdgeGrid::Grid &grid_lslice, - const Line &original_travel, - const Polyline &result_travel, - const size_t intersection_count) + const ExPolygons &lslices_offset, + const std::vector &lslices_offset_bboxes, + const EdgeGrid::Grid &grid_lslices_offset, + const Line &original_travel, + const Polyline &result_travel, + const size_t intersection_count) { - const ExPolygons &lslices = gcodegen.layer()->lslices; - const std::vector &lslices_bboxes = gcodegen.layer()->lslices_bboxes; bool z_lift_enabled = gcodegen.config().z_hop.get_at(gcodegen.writer().extruder()->id()) > 0.; bool wipe_needed = false; @@ -714,16 +751,16 @@ static bool need_wipe(const GCode &gcodegen, // The original layer is intersected with defined boundaries. Then it is necessary to make a detailed test. // If the z-lift is enabled, then a wipe is needed when the original travel leads above the holes. if (z_lift_enabled) { - if (any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, original_travel)) { + if (any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, original_travel)) { // Check if original_travel and result_travel are not same. // If both are the same, then it is possible to skip testing of result_travel wipe_needed = !(result_travel.size() > 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) && - !any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel); + !any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel); } else { wipe_needed = true; } } else { - wipe_needed = !any_expolygon_contains(lslices, lslices_bboxes, grid_lslice, result_travel); + wipe_needed = !any_expolygon_contains(lslices_offset, lslices_offset_bboxes, grid_lslices_offset, result_travel); } } @@ -945,55 +982,71 @@ static std::vector contour_distance(const EdgeGrid::Grid &grid, // ExPolygons are handled one by one so returned ExPolygons could intersect. static ExPolygons inner_offset(const ExPolygons &ex_polygons, double offset) { - double min_contour_width = 2. * offset + SCALED_EPSILON; - double search_radius = 2. * (offset + min_contour_width); - ExPolygons ex_poly_result = ex_polygons; + const std::vector min_contour_width_values = {offset / 2., offset, 2. * offset + SCALED_EPSILON}; + ExPolygons ex_poly_result = ex_polygons; resample_expolygons(ex_poly_result, offset / 2, scaled(0.5)); for (ExPolygon &ex_poly : ex_poly_result) { BoundingBox bbox(get_extents(ex_poly)); bbox.offset(SCALED_EPSILON); - EdgeGrid::Grid grid; - grid.set_bbox(bbox); - grid.create(ex_poly, coord_t(0.7 * search_radius)); - std::vector> ex_poly_distances; - precompute_expolygon_distances(ex_poly, ex_poly_distances); + // Filter out expolygons smaller than 0.1mm^2 + if (Vec2d bbox_size = bbox.size().cast(); bbox_size.x() * bbox_size.y() < Slic3r::sqr(scale_(0.1f))) + continue; - std::vector> offsets; - offsets.reserve(ex_poly.holes.size() + 1); - for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) { - const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1]; - assert(poly.is_counter_clockwise() == (idx_contour == 0)); - std::vector distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius); - for (float &distance : distances) { - if (distance < min_contour_width) - distance = 0.f; - else if (distance > min_contour_width + 2. * offset) - distance = - float(offset); - else - distance = - (distance - float(min_contour_width)) / 2.f; - } - offsets.emplace_back(distances); - } + for (const double &min_contour_width : min_contour_width_values) { + const size_t min_contour_width_idx = &min_contour_width - &min_contour_width_values.front(); + const double search_radius = 2. * (offset + min_contour_width); - ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets); - // If variable_offset_inner_ex produces empty result, then original ex_polygon is used - if (offset_ex_poly.size() == 1) { - ex_poly = std::move(offset_ex_poly.front()); - } else if (offset_ex_poly.size() > 1) { - // fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces - // tiny artefacts polygons, so these artefacts are removed. - double max_area = offset_ex_poly.front().area(); - size_t max_area_idx = 0; - for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) { - double area = offset_ex_poly[poly_idx].area(); - if (max_area < area) { - max_area = area; - max_area_idx = poly_idx; + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(ex_poly, coord_t(0.7 * search_radius)); + + std::vector> ex_poly_distances; + precompute_expolygon_distances(ex_poly, ex_poly_distances); + + std::vector> offsets; + offsets.reserve(ex_poly.holes.size() + 1); + for (size_t idx_contour = 0; idx_contour <= ex_poly.holes.size(); ++idx_contour) { + const Polygon &poly = (idx_contour == 0) ? ex_poly.contour : ex_poly.holes[idx_contour - 1]; + assert(poly.is_counter_clockwise() == (idx_contour == 0)); + std::vector distances = contour_distance(grid, ex_poly_distances[idx_contour], idx_contour, poly, offset, search_radius); + for (float &distance : distances) { + if (distance < min_contour_width) + distance = 0.f; + else if (distance > min_contour_width + 2. * offset) + distance = -float(offset); + else + distance = -(distance - float(min_contour_width)) / 2.f; } + offsets.emplace_back(distances); + } + + ExPolygons offset_ex_poly = variable_offset_inner_ex(ex_poly, offsets); + // If variable_offset_inner_ex produces empty result, then original ex_polygon is used + if (offset_ex_poly.size() == 1 && offset_ex_poly.front().holes.size() == ex_poly.holes.size()) { + ex_poly = std::move(offset_ex_poly.front()); + break; + } else if ((min_contour_width_idx + 1) < min_contour_width_values.size()) { + continue; // Try the next round with bigger min_contour_width. + } else if (offset_ex_poly.size() == 1) { + ex_poly = std::move(offset_ex_poly.front()); + break; + } else if (offset_ex_poly.size() > 1) { + // fix_after_inner_offset called inside variable_offset_inner_ex sometimes produces + // tiny artefacts polygons, so these artefacts are removed. + double max_area = offset_ex_poly.front().area(); + size_t max_area_idx = 0; + for (size_t poly_idx = 1; poly_idx < offset_ex_poly.size(); ++poly_idx) { + double area = offset_ex_poly[poly_idx].area(); + if (max_area < area) { + max_area = area; + max_area_idx = poly_idx; + } + } + ex_poly = std::move(offset_ex_poly[max_area_idx]); + break; } - ex_poly = std::move(offset_ex_poly[max_area_idx]); } } return ex_poly_result; @@ -1133,10 +1186,8 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & Vec2d startf = start.cast(); Vec2d endf = end .cast(); - const ExPolygons &lslices = gcodegen.layer()->lslices; - const std::vector &lslices_bboxes = gcodegen.layer()->lslices_bboxes; - bool is_support_layer = (dynamic_cast(gcodegen.layer()) != nullptr); - if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) { + bool is_support_layer = dynamic_cast(gcodegen.layer()) != nullptr; + if (!use_external && (is_support_layer || (!m_lslices_offset.empty() && !any_expolygon_contains(m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel)))) { // Initialize m_internal only when it is necessary. if (m_internal.boundaries.empty()) init_boundary(&m_internal, to_polygons(get_boundary(*gcodegen.layer()))); @@ -1186,7 +1237,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & } else if (max_detour_length_exceeded) { *could_be_wipe_disabled = false; } else - *could_be_wipe_disabled = !need_wipe(gcodegen, m_grid_lslice, travel, result_pl, travel_intersection_count); + *could_be_wipe_disabled = !need_wipe(gcodegen, m_lslices_offset, m_lslices_offset_bboxes, m_grid_lslices_offset, travel, result_pl, travel_intersection_count); return result_pl; } @@ -1197,13 +1248,21 @@ void AvoidCrossingPerimeters::init_layer(const Layer &layer) { m_internal.clear(); m_external.clear(); + m_lslices_offset.clear(); + m_lslices_offset_bboxes.clear(); + + float perimeter_offset = -get_external_perimeter_width(layer) / float(2.); + m_lslices_offset = offset_ex(layer.lslices, perimeter_offset); + + m_lslices_offset_bboxes.reserve(m_lslices_offset.size()); + for (const ExPolygon &ex_poly : m_lslices_offset) + m_lslices_offset_bboxes.emplace_back(get_extents(ex_poly)); BoundingBox bbox_slice(get_extents(layer.lslices)); bbox_slice.offset(SCALED_EPSILON); - m_grid_lslice.set_bbox(bbox_slice); - //FIXME 1mm grid? - m_grid_lslice.create(layer.lslices, coord_t(scale_(1.))); + m_grid_lslices_offset.set_bbox(bbox_slice); + m_grid_lslices_offset.create(m_lslices_offset, coord_t(scale_(1.))); } #if 0 diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index b67642c5d8..5d85aace7f 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2020 - 2022 Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_AvoidCrossingPerimeters_hpp_ #define slic3r_AvoidCrossingPerimeters_hpp_ @@ -58,8 +62,11 @@ private: // we enable it by default for the first travel move in print bool m_disabled_once { true }; + // Lslices offseted by half an external perimeter width. Used for detection if line or polyline is inside of any polygon. + ExPolygons m_lslices_offset; + std::vector m_lslices_offset_bboxes; // Used for detection of line or polyline is inside of any polygon. - EdgeGrid::Grid m_grid_lslice; + EdgeGrid::Grid m_grid_lslices_offset; // Store all needed data for travels inside object Boundary m_internal; // Store all needed data for travels outside object