diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 96eff24e73..47da80b6ce 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -46,8 +46,6 @@ using namespace std::literals::string_view_literals; #endif #include -#include -#include namespace Slic3r { @@ -232,7 +230,7 @@ namespace Slic3r { // Move over the wipe tower. // Retract for a tool change, using the toolchange retract value and setting the priming extra length. gcode += gcodegen.retract(true); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); gcode += gcodegen.travel_to( wipe_tower_point_to_object_point(gcodegen, start_pos), erMixed, @@ -327,7 +325,7 @@ namespace Slic3r { } // Let the planner know we are traveling between objects. - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); return gcode; } @@ -1175,12 +1173,12 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Move to the origin position for the copy we're going to print. // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer - m_avoid_crossing_perimeters.use_external_mp_once = true; + m_avoid_crossing_perimeters.use_external_mp_once(); _write(file, this->retract()); _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); m_enable_cooling_markers = true; // Disable motion planner when traveling to first object point. - m_avoid_crossing_perimeters.disable_once = true; + m_avoid_crossing_perimeters.disable_once(); // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. @@ -1998,7 +1996,7 @@ void GCode::process_layer( if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { const std::pair loops = loops_it->second; this->set_origin(0., 0.); - m_avoid_crossing_perimeters.use_external_mp = true; + m_avoid_crossing_perimeters.use_external_mp(); Flow layer_skirt_flow(print.skirt_flow()); layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])); double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); @@ -2012,23 +2010,23 @@ void GCode::process_layer( //FIXME using the support_material_speed of the 1st object printed. gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value); } - m_avoid_crossing_perimeters.use_external_mp = false; + m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). if (first_layer && loops.first == 0) - m_avoid_crossing_perimeters.disable_once = true; + m_avoid_crossing_perimeters.disable_once(); } // Extrude brim with the extruder of the 1st region. if (! m_brim_done) { this->set_origin(0., 0.); - m_avoid_crossing_perimeters.use_external_mp = true; + m_avoid_crossing_perimeters.use_external_mp(); for (const ExtrusionEntity *ee : print.brim().entities) { gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value); } m_brim_done = true; - m_avoid_crossing_perimeters.use_external_mp = false; + m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point. - m_avoid_crossing_perimeters.disable_once = true; + m_avoid_crossing_perimeters.disable_once(); } @@ -2055,7 +2053,7 @@ void GCode::process_layer( const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift; std::pair this_object_copy(&instance_to_print.print_object, offset); if (m_last_obj_copy != this_object_copy) - m_avoid_crossing_perimeters.use_external_mp_once = true; + m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) { @@ -2647,9 +2645,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string /* Define the travel move as a line between current position and the taget point. This is expressed in print coordinates, so it will need to be translated by this->origin in order to get G-code coordinates. */ - Polyline travel; - travel.append(this->last_pos()); - travel.append(point); + Polyline travel { this->last_pos(), point }; // check whether a straight travel move would need retraction bool needs_retraction = this->needs_retraction(travel, role); @@ -2660,31 +2656,28 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string // multi-hop travel path inside the configuration space if (needs_retraction && m_config.avoid_crossing_perimeters - && ! m_avoid_crossing_perimeters.disable_once) { + && ! m_avoid_crossing_perimeters.disabled_once()) { 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); //if (needs_retraction && m_layer_index > 1) exit(0); } // Re-allow avoid_crossing_perimeters for the next travel moves - m_avoid_crossing_perimeters.disable_once = false; - m_avoid_crossing_perimeters.use_external_mp_once = false; + m_avoid_crossing_perimeters.reset_once_modifiers(); // 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) + if (m_config.avoid_crossing_perimeters && 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. // Because of it, it is necessary to call avoid crossing perimeters for the path between previous last_post and last_post after calling retraction() - if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters && !m_avoid_crossing_perimeters.disable_once) { + if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters) { Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, last_post_before_retract); - retract_travel.points.reserve(retract_travel.points.size() + travel.points.size()); append(retract_travel.points, travel.points); travel = std::move(retract_travel); } @@ -2693,11 +2686,10 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string m_wipe.reset_path(); // use G1 because we rely on paths being straight (G0 may make round paths) - Lines lines = travel.lines(); - if (! lines.empty()) { - for (const Line &line : lines) - gcode += m_writer.travel_to_xy(this->point_to_gcode(line.b), comment); - this->set_last_pos(lines.back().b); + if (travel.size() >= 2) { + for (size_t i = 1; i < travel.size(); ++ i) + gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment); + this->set_last_pos(travel.points.back()); } return gcode; } diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index 6a211e05bc..7c02135567 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -1,8 +1,6 @@ #include "../Layer.hpp" #include "../GCode.hpp" #include "../EdgeGrid.hpp" -#include "../Geometry.hpp" -#include "../ShortestPath.hpp" #include "../Print.hpp" #include "../Polygon.hpp" #include "../ExPolygon.hpp" @@ -10,15 +8,80 @@ #include "../SVG.hpp" #include "AvoidCrossingPerimeters.hpp" -#include #include #include -#include -#include - namespace Slic3r { +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; +}; + +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) + : border_idx(border_idx), line_idx(line_idx), point_transformed(point_transformed), point(point){}; + + inline bool operator<(const Intersection &other) const { return this->point_transformed.x() < other.point_transformed.x(); } +}; + +struct AllIntersectionsVisitor +{ + AllIntersectionsVisitor(const EdgeGrid::Grid &grid, std::vector &intersections) + : grid(grid), intersections(intersections) + {} + + AllIntersectionsVisitor(const EdgeGrid::Grid &grid, + std::vector &intersections, + const Matrix2d &transform_to_x_axis, + const Line &travel_line) + : grid(grid), intersections(intersections), transform_to_x_axis(transform_to_x_axis), travel_line(travel_line) + {} + + void reset() { + intersection_set.clear(); + } + + bool operator()(coord_t iy, coord_t ix) + { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; + ++it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = grid.segment(*it_contour_and_segment); + + Point intersection_point; + if (travel_line.intersection(Line(segment.first, segment.second), &intersection_point) && + intersection_set.find(*it_contour_and_segment) == intersection_set.end()) { + intersections.emplace_back(it_contour_and_segment->first, it_contour_and_segment->second, + (transform_to_x_axis * intersection_point.cast()).cast(), intersection_point); + intersection_set.insert(*it_contour_and_segment); + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + std::vector &intersections; + Matrix2d transform_to_x_axis; + Line travel_line; + std::unordered_set, boost::hash>> intersection_set; +}; + // Create a rotation matrix for projection on the given vector static Matrix2d rotation_by_direction(const Point &direction) { @@ -185,16 +248,16 @@ static std::pair split_expolygon(const ExPolygons &ex_polygo return std::make_pair(std::move(contours), std::move(holes)); } -static Polyline to_polyline(const std::vector &travel) +static Polyline to_polyline(const std::vector &travel) { Polyline result; result.points.reserve(travel.size()); - for (const AvoidCrossingPerimeters::TravelPoint &t_point : travel) + for (const TravelPoint &t_point : travel) result.append(t_point.point); return result; } -static double travel_length(const std::vector &travel) { +static double travel_length(const std::vector &travel) { double total_length = 0; for (size_t idx = 1; idx < travel.size(); ++idx) total_length += (travel[idx].point - travel[idx - 1].point).cast().norm(); @@ -203,11 +266,11 @@ static double travel_length(const std::vector &intersections, - const std::string &path) +static void export_travel_to_svg(const Polygons &boundary, + const Line &original_travel, + const Polyline &result_travel, + const std::vector &intersections, + const std::string &path) { BoundingBox bbox = get_extents(boundary); ::Slic3r::SVG svg(path, bbox); @@ -217,21 +280,21 @@ static void export_travel_to_svg(const Polygons svg.draw(original_travel.a, "black"); svg.draw(original_travel.b, "grey"); - for (const AvoidCrossingPerimeters::Intersection &intersection : intersections) + for (const Intersection &intersection : intersections) svg.draw(intersection.point, "lightseagreen"); } -static void export_travel_to_svg(const Polygons &boundary, - const Line &original_travel, - const std::vector &result_travel, - const std::vector &intersections, - const std::string &path) +static void export_travel_to_svg(const Polygons &boundary, + const Line &original_travel, + const std::vector &result_travel, + const std::vector &intersections, + const std::string &path) { export_travel_to_svg(boundary, original_travel, to_polyline(result_travel), intersections, path); } #endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */ -ExPolygons AvoidCrossingPerimeters::get_boundary(const Layer &layer) +static ExPolygons get_boundary(const Layer &layer) { const float perimeter_spacing = get_perimeter_spacing(layer); const float perimeter_offset = perimeter_spacing / 2.f; @@ -291,7 +354,7 @@ ExPolygons AvoidCrossingPerimeters::get_boundary(const Layer &layer) return result_boundary; } -ExPolygons AvoidCrossingPerimeters::get_boundary_external(const Layer &layer) +static ExPolygons get_boundary_external(const Layer &layer) { const float perimeter_spacing = get_perimeter_spacing_external(layer); const float perimeter_offset = perimeter_spacing / 2.f; @@ -328,11 +391,12 @@ ExPolygons AvoidCrossingPerimeters::get_boundary_external(const Layer &layer) } // Returns a direction of the shortest path along the polygon boundary -AvoidCrossingPerimeters::Direction AvoidCrossingPerimeters::get_shortest_direction(const Lines &lines, - const size_t start_idx, - const size_t end_idx, - const Point &intersection_first, - const Point &intersection_last) +enum class Direction { Forward, Backward }; +static Direction get_shortest_direction(const Lines &lines, + const size_t start_idx, + const size_t end_idx, + const Point &intersection_first, + const Point &intersection_last) { double total_length_forward = (lines[start_idx].b - intersection_first).cast().norm(); double total_length_backward = (lines[start_idx].a - intersection_first).cast().norm(); @@ -358,165 +422,14 @@ AvoidCrossingPerimeters::Direction AvoidCrossingPerimeters::get_shortest_directi return (total_length_forward < total_length_backward) ? Direction::Forward : Direction::Backward; } -std::vector AvoidCrossingPerimeters::simplify_travel(const EdgeGrid::Grid &edge_grid, - const std::vector &travel, - const Polygons &boundaries, - const bool use_heuristics) -{ - struct Visitor - { - Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} +static std::vector simplify_travel(const EdgeGrid::Grid& edge_grid, const std::vector& travel, const Polygons& boundaries, const bool use_heuristics); - bool operator()(coord_t iy, coord_t ix) - { - assert(pt_current != nullptr); - assert(pt_next != nullptr); - // Called with a row and colum of the grid cell, which is intersected by a line. - auto cell_data_range = grid.cell_data_range(iy, ix); - this->intersect = false; - for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { - // End points of the line segment and their vector. - auto segment = grid.segment(*it_contour_and_segment); - if (Geometry::segments_intersect(segment.first, segment.second, *pt_current, *pt_next)) { - this->intersect = true; - return false; - } - } - // Continue traversing the grid along the edge. - return true; - } - - const EdgeGrid::Grid &grid; - const Slic3r::Point *pt_current = nullptr; - const Slic3r::Point *pt_next = nullptr; - bool intersect = false; - } visitor(edge_grid); - - std::vector simplified_path; - simplified_path.reserve(travel.size()); - simplified_path.emplace_back(travel.front()); - - // Try to skip some points in the path. - for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) { - const Point ¤t_point = travel[point_idx - 1].point; - TravelPoint next = travel[point_idx]; - - 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; - } - - visitor.pt_next = &travel[point_idx_2].point; - edge_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); - } - - if(use_heuristics) { - simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries); - std::reverse(simplified_path.begin(),simplified_path.end()); - simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries); - std::reverse(simplified_path.begin(),simplified_path.end()); - } - - return simplified_path; -} - -std::vector AvoidCrossingPerimeters::simplify_travel_heuristics(const EdgeGrid::Grid &edge_grid, - const std::vector &travel, - const Polygons &boundaries) -{ - std::vector simplified_path; - std::vector intersections; - AllIntersectionsVisitor visitor(edge_grid, intersections); - simplified_path.reserve(travel.size()); - simplified_path.emplace_back(travel.front()); - for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) { - // Skip all indexes on the same polygon - while (point_idx < travel.size() && travel[point_idx - 1].border_idx == travel[point_idx].border_idx) { - simplified_path.emplace_back(travel[point_idx]); - point_idx++; - } - - if (point_idx < travel.size()) { - const TravelPoint ¤t = travel[point_idx - 1]; - const TravelPoint &next = travel[point_idx]; - TravelPoint new_next = next; - size_t new_point_idx = point_idx; - double path_length = (next.point - current.point).cast().norm(); - double new_path_shorter_by = 0.; - size_t border_idx_change_count = 0; - std::vector shortcut; - for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) { - const TravelPoint &possible_new_next = travel[point_idx_2]; - if (travel[point_idx_2 - 1].border_idx != travel[point_idx_2].border_idx) - border_idx_change_count++; - - if (border_idx_change_count >= 2) - break; - - path_length += (possible_new_next.point - travel[point_idx_2 - 1].point).cast().norm(); - double shortcut_length = (possible_new_next.point - current.point).cast().norm(); - if ((path_length - shortcut_length) <= scale_(10.0)) - continue; - - intersections.clear(); - visitor.reset(); - visitor.travel_line.a = current.point; - visitor.travel_line.b = possible_new_next.point; - visitor.transform_to_x_axis = rotation_by_direction(visitor.travel_line.vector()); - edge_grid.visit_cells_intersecting_line(visitor.travel_line.a, visitor.travel_line.b, visitor); - if (!intersections.empty()) { - std::sort(intersections.begin(), intersections.end()); - size_t last_border_idx_count = 0; - for (const Intersection &intersection : intersections) - if (int(intersection.border_idx) == possible_new_next.border_idx) - ++last_border_idx_count; - - if (last_border_idx_count > 0) - continue; - - std::vector possible_shortcut; - avoid_perimeters(boundaries, edge_grid, current.point, possible_new_next.point, false, &possible_shortcut); - double shortcut_travel = travel_length(possible_shortcut); - if (path_length > shortcut_travel && (path_length - shortcut_travel) > new_path_shorter_by) { - new_path_shorter_by = path_length - shortcut_travel; - shortcut = possible_shortcut; - new_next = possible_new_next; - new_point_idx = point_idx_2; - } - } - } - - if (!shortcut.empty()) { - assert(shortcut.size() >= 2); - simplified_path.insert(simplified_path.end(), shortcut.begin() + 1, shortcut.end() - 1); - point_idx = new_point_idx; - } - - simplified_path.emplace_back(new_next); - } - } - - return simplified_path; -} - -size_t AvoidCrossingPerimeters::avoid_perimeters(const Polygons &boundaries, - const EdgeGrid::Grid &edge_grid, - const Point &start, - const Point &end, - const bool use_heuristics, - std::vector *result_out) +static size_t avoid_perimeters(const Polygons &boundaries, + const EdgeGrid::Grid &edge_grid, + const Point &start, + const Point &end, + const bool use_heuristics, + std::vector *result_out) { const Point direction = end - start; Matrix2d transform_to_x_axis = rotation_by_direction(direction); @@ -608,10 +521,164 @@ size_t AvoidCrossingPerimeters::avoid_perimeters(const Polygons &bound return intersections.size(); } -bool AvoidCrossingPerimeters::need_wipe(const GCode & gcodegen, - const Line & original_travel, - const Polyline &result_travel, - const size_t intersection_count) +static std::vector simplify_travel_heuristics(const EdgeGrid::Grid &edge_grid, + const std::vector &travel, + const Polygons &boundaries) +{ + std::vector simplified_path; + std::vector intersections; + AllIntersectionsVisitor visitor(edge_grid, intersections); + simplified_path.reserve(travel.size()); + simplified_path.emplace_back(travel.front()); + for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) { + // Skip all indexes on the same polygon + while (point_idx < travel.size() && travel[point_idx - 1].border_idx == travel[point_idx].border_idx) { + simplified_path.emplace_back(travel[point_idx]); + point_idx++; + } + + if (point_idx < travel.size()) { + const TravelPoint ¤t = travel[point_idx - 1]; + const TravelPoint &next = travel[point_idx]; + TravelPoint new_next = next; + size_t new_point_idx = point_idx; + double path_length = (next.point - current.point).cast().norm(); + double new_path_shorter_by = 0.; + size_t border_idx_change_count = 0; + std::vector shortcut; + for (size_t point_idx_2 = point_idx + 1; point_idx_2 < travel.size(); ++point_idx_2) { + const TravelPoint &possible_new_next = travel[point_idx_2]; + if (travel[point_idx_2 - 1].border_idx != travel[point_idx_2].border_idx) + border_idx_change_count++; + + if (border_idx_change_count >= 2) + break; + + path_length += (possible_new_next.point - travel[point_idx_2 - 1].point).cast().norm(); + double shortcut_length = (possible_new_next.point - current.point).cast().norm(); + if ((path_length - shortcut_length) <= scale_(10.0)) + continue; + + intersections.clear(); + visitor.reset(); + visitor.travel_line.a = current.point; + visitor.travel_line.b = possible_new_next.point; + visitor.transform_to_x_axis = rotation_by_direction(visitor.travel_line.vector()); + edge_grid.visit_cells_intersecting_line(visitor.travel_line.a, visitor.travel_line.b, visitor); + if (!intersections.empty()) { + std::sort(intersections.begin(), intersections.end()); + size_t last_border_idx_count = 0; + for (const Intersection &intersection : intersections) + if (int(intersection.border_idx) == possible_new_next.border_idx) + ++last_border_idx_count; + + if (last_border_idx_count > 0) + continue; + + std::vector possible_shortcut; + avoid_perimeters(boundaries, edge_grid, current.point, possible_new_next.point, false, &possible_shortcut); + double shortcut_travel = travel_length(possible_shortcut); + if (path_length > shortcut_travel && (path_length - shortcut_travel) > new_path_shorter_by) { + new_path_shorter_by = path_length - shortcut_travel; + shortcut = possible_shortcut; + new_next = possible_new_next; + new_point_idx = point_idx_2; + } + } + } + + if (!shortcut.empty()) { + assert(shortcut.size() >= 2); + simplified_path.insert(simplified_path.end(), shortcut.begin() + 1, shortcut.end() - 1); + point_idx = new_point_idx; + } + + simplified_path.emplace_back(new_next); + } + } + + return simplified_path; +} + +static std::vector simplify_travel(const EdgeGrid::Grid &edge_grid, + const std::vector &travel, + const Polygons &boundaries, + const bool use_heuristics) +{ + struct Visitor + { + Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} + + bool operator()(coord_t iy, coord_t ix) + { + assert(pt_current != nullptr); + assert(pt_next != nullptr); + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + this->intersect = false; + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = grid.segment(*it_contour_and_segment); + if (Geometry::segments_intersect(segment.first, segment.second, *pt_current, *pt_next)) { + this->intersect = true; + return false; + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + const Slic3r::Point *pt_current = nullptr; + const Slic3r::Point *pt_next = nullptr; + bool intersect = false; + } visitor(edge_grid); + + std::vector simplified_path; + simplified_path.reserve(travel.size()); + simplified_path.emplace_back(travel.front()); + + // Try to skip some points in the path. + for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) { + const Point ¤t_point = travel[point_idx - 1].point; + TravelPoint next = travel[point_idx]; + + 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; + } + + visitor.pt_next = &travel[point_idx_2].point; + edge_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); + } + + if(use_heuristics) { + simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries); + std::reverse(simplified_path.begin(),simplified_path.end()); + simplified_path = simplify_travel_heuristics(edge_grid, simplified_path, boundaries); + std::reverse(simplified_path.begin(),simplified_path.end()); + } + + return simplified_path; +} + +static bool need_wipe(const GCode &gcodegen, + const ExPolygons &slice, + 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; @@ -622,19 +689,19 @@ bool AvoidCrossingPerimeters::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(m_slice, original_travel)) { + if (any_expolygon_contains(slice, 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 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); + wipe_needed = !any_expolygon_contains(slice, result_travel); } } else { wipe_needed = true; } } else { - wipe_needed = !any_expolygon_contains(m_slice, result_travel); + wipe_needed = !any_expolygon_contains(slice, result_travel); } } @@ -646,7 +713,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & { // 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. - bool use_external = this->use_external_mp || this->use_external_mp_once; + bool use_external = m_use_external_mp || m_use_external_mp_once; Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0); Point start = gcodegen.last_pos() + scaled_origin; Point end = point + scaled_origin; @@ -659,9 +726,9 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & std::vector result; auto [start_clamped, end_clamped] = clamp_endpoints_by_bounding_box(use_external ? m_bbox_external : m_bbox, start, end); if (use_external) - travel_intersection_count = this->avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, true, &result); + travel_intersection_count = avoid_perimeters(m_boundaries_external, m_grid_external, start_clamped, end_clamped, true, &result); else - travel_intersection_count = this->avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, true, &result); + travel_intersection_count = avoid_perimeters(m_boundaries, m_grid, start_clamped, end_clamped, true, &result); result_pl = to_polyline(result); } @@ -678,7 +745,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point & result_pl.translate(-scaled_origin); *could_be_wipe_disabled = false; } else - *could_be_wipe_disabled = !need_wipe(gcodegen, travel, result_pl, travel_intersection_count); + *could_be_wipe_disabled = !need_wipe(gcodegen, m_slice, travel, result_pl, travel_intersection_count); return result_pl; } diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index 0f99108f9e..d33311a908 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -5,120 +5,40 @@ #include "../ExPolygon.hpp" #include "../EdgeGrid.hpp" -#include -#include - namespace Slic3r { // Forward declarations. class GCode; class Layer; class Point; -class Print; -class PrintObject; - -struct PrintInstance; -using PrintObjectPtrs = std::vector; class AvoidCrossingPerimeters { public: - struct Intersection + // Routing around the objects vs. inside a single object. + void use_external_mp(bool use = true) { m_use_external_mp = use; }; + void use_external_mp_once() { m_use_external_mp_once = true; } + void disable_once() { m_disabled_once = true; } + bool disabled_once() const { return m_disabled_once; } + void reset_once_modifiers() { m_use_external_mp_once = false; m_disabled_once = false; } + + void init_layer(const Layer &layer); + + Polyline travel_to(const GCode& gcodegen, const Point& point) { - // 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; + bool could_be_wipe_disabled; + return this->travel_to(gcodegen, point, &could_be_wipe_disabled); + } - Intersection(size_t border_idx, size_t line_idx, const Point &point_transformed, const Point &point) - : border_idx(border_idx), line_idx(line_idx), point_transformed(point_transformed), point(point){}; - - inline bool operator<(const Intersection &other) const { return this->point_transformed.x() < other.point_transformed.x(); } - }; - - 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; - }; - - struct AllIntersectionsVisitor - { - AllIntersectionsVisitor(const EdgeGrid::Grid &grid, std::vector &intersections) - : grid(grid), intersections(intersections) - {} - - AllIntersectionsVisitor(const EdgeGrid::Grid &grid, - std::vector &intersections, - const Matrix2d &transform_to_x_axis, - const Line &travel_line) - : grid(grid), intersections(intersections), transform_to_x_axis(transform_to_x_axis), travel_line(travel_line) - {} - - void reset() { - intersection_set.clear(); - } - - bool operator()(coord_t iy, coord_t ix) - { - // Called with a row and colum of the grid cell, which is intersected by a line. - auto cell_data_range = grid.cell_data_range(iy, ix); - for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; - ++it_contour_and_segment) { - // End points of the line segment and their vector. - auto segment = grid.segment(*it_contour_and_segment); - - Point intersection_point; - if (travel_line.intersection(Line(segment.first, segment.second), &intersection_point) && - intersection_set.find(*it_contour_and_segment) == intersection_set.end()) { - intersections.emplace_back(it_contour_and_segment->first, it_contour_and_segment->second, - (transform_to_x_axis * intersection_point.cast()).cast(), intersection_point); - intersection_set.insert(*it_contour_and_segment); - } - } - // Continue traversing the grid along the edge. - return true; - } - - const EdgeGrid::Grid &grid; - std::vector &intersections; - Matrix2d transform_to_x_axis; - Line travel_line; - std::unordered_set, boost::hash>> intersection_set; - }; - - enum class Direction { Forward, Backward }; + Polyline travel_to(const GCode& gcodegen, const Point& point, bool* could_be_wipe_disabled); private: - static ExPolygons get_boundary(const Layer &layer); - - static ExPolygons get_boundary_external(const Layer &layer); - - static Direction get_shortest_direction( - const Lines &lines, const size_t start_idx, const size_t end_idx, const Point &intersection_first, const Point &intersection_last); - - static std::vector simplify_travel(const EdgeGrid::Grid &edge_grid, - const std::vector &travel, - const Polygons &boundaries, - const bool use_heuristics); - - static std::vector simplify_travel_heuristics(const EdgeGrid::Grid &edge_grid, - const std::vector &travel, - const Polygons &boundaries); - - static size_t avoid_perimeters(const Polygons &boundaries, - const EdgeGrid::Grid &grid, - const Point &start, - const Point &end, - const bool use_heuristics, - std::vector *result_out); - - bool need_wipe(const GCode &gcodegen, const Line &original_travel, const Polyline &result_travel, const size_t intersection_count); + bool m_use_external_mp { false }; + // just for the next travel move + bool m_use_external_mp_once { false }; + // this flag disables avoid_crossing_perimeters just for the next travel move + // we enable it by default for the first travel move in print + bool m_disabled_once { true }; // Slice of layer with elephant foot compensation ExPolygons m_slice; @@ -132,27 +52,6 @@ private: BoundingBox m_bbox_external; EdgeGrid::Grid m_grid; EdgeGrid::Grid m_grid_external; - -public: - // this flag triggers the use of the external configuration space - bool use_external_mp { false }; - // just for the next travel move - bool use_external_mp_once { false }; - // this flag disables avoid_crossing_perimeters just for the next travel move - // we enable it by default for the first travel move in print - bool disable_once { true }; - - AvoidCrossingPerimeters() = default; - - Polyline travel_to(const GCode& gcodegen, const Point& point) - { - bool could_be_wipe_disabled; - return this->travel_to(gcodegen, point, &could_be_wipe_disabled); - } - - Polyline travel_to(const GCode& gcodegen, const Point& point, bool* could_be_wipe_disabled); - - void init_layer(const Layer &layer); }; } // namespace Slic3r