From ef9de07740622e238e21d4c6a795eb9765408a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 22 Oct 2020 06:20:38 +0200 Subject: [PATCH] Disabling wipe for avoid crossing perimeters --- src/libslic3r/GCode.cpp | 9 +- src/libslic3r/GCode.hpp | 1 + .../GCode/AvoidCrossingPerimeters.cpp | 105 +++++++++++++----- .../GCode/AvoidCrossingPerimeters.hpp | 36 +++++- 4 files changed, 119 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f152edebf..f1c8f5bbf 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2662,14 +2662,16 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string travel.append(point); // check whether a straight travel move would need retraction - bool needs_retraction = this->needs_retraction(travel, role); + bool needs_retraction = this->needs_retraction(travel, role); + // check whether wipe could be disabled without causing visible stringing + bool could_be_wipe_disabled = false; // if a retraction would be needed, try to use avoid_crossing_perimeters to plan a // multi-hop travel path inside the configuration space if (needs_retraction && m_config.avoid_crossing_perimeters && ! m_avoid_crossing_perimeters.disable_once) { - travel = m_avoid_crossing_perimeters.travel_to(*this, point); + travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); // check again whether the new travel path still needs a retraction needs_retraction = this->needs_retraction(travel, role); @@ -2683,6 +2685,9 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string // generate G-code for the travel move std::string gcode; if (needs_retraction) { + if (m_config.avoid_crossing_perimeters && !m_avoid_crossing_perimeters.disable_once && could_be_wipe_disabled) + m_wipe.reset_path(); + Point last_post_before_retract = this->last_pos(); gcode += this->retract(); // When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 60c1dd9e1..d13ace30b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -156,6 +156,7 @@ public: const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } + const GCodeWriter& writer() const { return m_writer; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } // Process a template through the placeholder parser, collect error messages to be reported diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index c5e43a65a..25fe631c2 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -115,6 +115,7 @@ static Matrix2d rotation_by_direction(const Point &direction) static Point find_first_different_vertex(const Polygon &polygon, const size_t point_idx, const Point &point, bool forward) { + assert(point_idx < polygon.size()); if (point != polygon.points[point_idx]) return polygon.points[point_idx]; @@ -137,8 +138,9 @@ static Vec2d three_points_inward_normal(const Point &left, const Point &middle, normal_2.normalize(); return (normal_1 + normal_2).normalized(); -}; +} +// Compute normal of the polygon's vertex in an inward direction static Vec2d get_polygon_vertex_inward_normal(const Polygon &polygon, const size_t point_idx) { const size_t left_idx = (point_idx <= 0) ? (polygon.size() - 1) : (point_idx - 1); @@ -149,12 +151,13 @@ static Vec2d get_polygon_vertex_inward_normal(const Polygon &polygon, const size return three_points_inward_normal(left, middle, right); } -// Compute offset of polygon's in a direction inward normal +// Compute offset of point_idx of the polygon in a direction of inward normal static Point get_polygon_vertex_offset(const Polygon &polygon, const size_t point_idx, const int offset) { return polygon.points[point_idx] + (get_polygon_vertex_inward_normal(polygon, point_idx) * double(offset)).cast(); } +// Compute offset (in the direction of inward normal) of the point(passed on "middle") based on the nearest points laying on the polygon (left_idx and right_idx). static Point get_middle_point_offset(const Polygon &polygon, const size_t left_idx, const size_t right_idx, const Point &middle, const int offset) { const Point &left = find_first_different_vertex(polygon, left_idx, middle, false); @@ -243,6 +246,15 @@ static coord_t get_perimeter_spacing_external(const Layer &layer) return perimeter_spacing; } +// Check if anyone of ExPolygons contains whole travel. +template static bool any_expolygon_contains(const ExPolygons &ex_polygons, const T &travel) +{ + for (const ExPolygon &ex_polygon : ex_polygons) + if (ex_polygon.contains(travel)) return true; + + return false; +} + ExPolygons AvoidCrossingPerimeters2::get_boundary(const Layer &layer) { const coord_t perimeter_spacing = get_perimeter_spacing(layer); @@ -321,6 +333,7 @@ ExPolygons AvoidCrossingPerimeters2::get_boundary_external(const Layer &layer) Polygons contours; Polygons holes; + contours.reserve(boundary.size()); for (ExPolygon &poly : boundary) { contours.emplace_back(poly.contour); append(holes, poly.holes); @@ -429,10 +442,11 @@ Polyline AvoidCrossingPerimeters2::simplify_travel(const EdgeGrid::Grid &edge_gr return optimized_comb_path; } -Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &boundaries, - const EdgeGrid::Grid &edge_grid, - const Point &start, - const Point &end) +size_t AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &boundaries, + const EdgeGrid::Grid &edge_grid, + const Point &start, + const Point &end, + Polyline *result_out) { const Point direction = end - start; Matrix2d transform_to_x_axis = rotation_by_direction(direction); @@ -496,12 +510,12 @@ Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &bounda // Append the nearest intersection into the path size_t left_idx = intersection_first.line_idx; size_t right_idx = (intersection_first.line_idx >= (boundaries[intersection_first.border_idx].points.size() - 1)) ? 0 : (intersection_first.line_idx + 1); + // Offset of the polygon's point using get_middle_point_offset is used to simplify calculation of intersection between boundary result.append(get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, SCALED_EPSILON)); Direction shortest_direction = get_shortest_direction(border_lines, intersection_first.line_idx, intersection_second.line_idx, intersection_first.point, intersection_second.point); // Append the path around the border into the path - // Offset of the polygon's point is used to simplify calculation of intersection between boundary if (shortest_direction == Direction::Forward) for (int line_idx = intersection_first.line_idx; line_idx != int(intersection_second.line_idx); line_idx = (((line_idx + 1) < int(border_lines.size())) ? (line_idx + 1) : 0)) @@ -524,10 +538,48 @@ Polyline AvoidCrossingPerimeters2::avoid_perimeters(const Polygons &bounda } result.append(end); - return simplify_travel(edge_grid, result); + if(!intersections.empty()) { + result = simplify_travel(edge_grid, result); + } + + append(result_out->points, result.points); + return intersections.size(); } -Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point) +bool AvoidCrossingPerimeters2::needs_wipe(const GCode & gcodegen, + const Line & original_travel, + const Polyline &result_travel, + const size_t intersection_count) +{ + bool z_lift_enabled = gcodegen.config().retract_lift.get_at(gcodegen.writer().extruder()->id()) > 0.; + bool wipe_needed = false; + + // If the original unmodified path doesn't have any intersection with boundary, then it is entirely inside the object otherwise is entirely + // outside the object. + if (intersection_count > 0) { + // 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(m_slice, original_travel)) { + // Check if original_travel and are not same result_travel + if (result_travel.size() == 2 && result_travel.first_point() == original_travel.a && result_travel.last_point() == original_travel.b) { + wipe_needed = false; + } else { + wipe_needed = !any_expolygon_contains(m_slice, result_travel); + } + } else { + wipe_needed = true; + } + } else { + wipe_needed = !any_expolygon_contains(m_slice, result_travel); + } + } + + return wipe_needed; +} + +// Plan travel, which avoids perimeter crossings by following the boundaries of the layer. +Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) { // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. @@ -536,14 +588,16 @@ Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point Point start = gcodegen.last_pos() + scaled_origin; Point end = point + scaled_origin; Polyline result; + size_t travel_intersection_count = 0; if (!check_if_could_cross_perimeters(use_external ? m_bbox_external : m_bbox, start, end)) { - result = Polyline({start, end}); + result = Polyline({start, end}); + travel_intersection_count = 0; } else { auto [start_clamped, end_clamped] = clamp_endpoints_by_bounding_box(use_external ? m_bbox_external : m_bbox, start, end); if (use_external) - result = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped); + travel_intersection_count = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, &result); else - result = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped); + travel_intersection_count = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, &result); } result.points.front() = start; @@ -554,33 +608,32 @@ Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point if ((max_detour_length > 0) && ((result.length() - travel.length()) > max_detour_length)) { result = Polyline({start, end}); } - if (use_external) + if (use_external) { result.translate(-scaled_origin); + *could_be_wipe_disabled = false; + } else + *could_be_wipe_disabled = !needs_wipe(gcodegen, travel, result, travel_intersection_count); + return result; } void AvoidCrossingPerimeters2::init_layer(const Layer &layer) { + m_slice.clear(); m_boundaries.clear(); m_boundaries_external.clear(); - ExPolygons boundaries = get_boundary(layer); - ExPolygons boundaries_external = get_boundary_external(layer); + for (const LayerRegion *layer_region : layer.regions()) + append(m_slice, (ExPolygons) layer_region->slices); - m_bbox = get_extents(boundaries); + m_boundaries = to_polygons(get_boundary(layer)); + m_boundaries_external = to_polygons(get_boundary_external(layer)); + + m_bbox = get_extents(m_boundaries); m_bbox.offset(SCALED_EPSILON); - m_bbox_external = get_extents(boundaries_external); + m_bbox_external = get_extents(m_boundaries_external); m_bbox_external.offset(SCALED_EPSILON); - for (const ExPolygon &ex_poly : boundaries) { - m_boundaries.emplace_back(ex_poly.contour); - append(m_boundaries, ex_poly.holes); - } - for (const ExPolygon &ex_poly : boundaries_external) { - m_boundaries_external.emplace_back(ex_poly.contour); - append(m_boundaries_external, ex_poly.holes); - } - m_grid.set_bbox(m_bbox); m_grid.create(m_boundaries, scale_(1.)); m_grid_external.set_bbox(m_bbox_external); diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index bc1f79654..e288a5b06 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -37,11 +37,17 @@ public: m_external_mp.reset(); m_layer_mp.reset(); } - void init_external_mp(const Print &print); - void init_layer_mp(const ExPolygons &islands) { m_layer_mp = Slic3r::make_unique(islands); } + virtual void init_external_mp(const Print &print); + virtual void init_layer_mp(const ExPolygons &islands) { m_layer_mp = Slic3r::make_unique(islands); } virtual Polyline travel_to(const GCode &gcodegen, const Point &point); + virtual Polyline travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) + { + *could_be_wipe_disabled = true; + return this->travel_to(gcodegen, point); + } + protected: // For initializing the regions to avoid. static Polygons collect_contours_all_layers(const PrintObjectPtrs &objects); @@ -55,9 +61,13 @@ class AvoidCrossingPerimeters2 : public AvoidCrossingPerimeters protected: struct Intersection { + // Index of the polygon containing this point of intersection. size_t border_idx; + // Index of the line on the polygon containing this point of intersection. size_t line_idx; + // Point of intersection projected on the travel path. Point point_transformed; + // Point of intersection. Point point; Intersection(size_t border_idx, size_t line_idx, const Point &point_transformed, const Point &point) @@ -78,11 +88,19 @@ private: static Polyline simplify_travel(const EdgeGrid::Grid &edge_grid, const Polyline &travel); - static Polyline avoid_perimeters(const Polygons &boundaries, const EdgeGrid::Grid &grid, const Point &start, const Point &end); + static size_t avoid_perimeters(const Polygons &boundaries, const EdgeGrid::Grid &grid, const Point &start, const Point &end, Polyline *result_out); + bool needs_wipe(const GCode &gcodegen, const Line &original_travel, const Polyline &result_travel, const size_t intersection_count); + + // Slice of layer with elephant foot compensation + ExPolygons m_slice; + // Collection of boundaries used for detection of crossing perimetrs for travels inside object Polygons m_boundaries; + // Collection of boundaries used for detection of crossing perimetrs for travels outside object Polygons m_boundaries_external; + // Bounding box of m_boundaries BoundingBox m_bbox; + // Bounding box of m_boundaries_external BoundingBox m_bbox_external; EdgeGrid::Grid m_grid; EdgeGrid::Grid m_grid_external; @@ -92,7 +110,17 @@ public: virtual ~AvoidCrossingPerimeters2() = default; - virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override; + // Used for disabling unnecessary calling collect_contours_all_layers + virtual void init_external_mp(const Print &print) override {}; + virtual void init_layer_mp(const ExPolygons &islands) override {}; + + virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override + { + bool could_be_wipe_disabled; + return this->travel_to(gcodegen, point, &could_be_wipe_disabled); + } + + virtual Polyline travel_to(const GCode &gcodegen, const Point &point, bool *needs_wipe) override; void init_layer(const Layer &layer); };