diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp
index 25fe631c2..e05b5b0ff 100644
--- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp
+++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp
@@ -255,10 +255,43 @@ template<class T> static bool any_expolygon_contains(const ExPolygons &ex_polygo
     return false;
 }
 
+static std::pair<Polygons, Polygons> split_expolygon(const ExPolygons &ex_polygons)
+{
+    Polygons contours, holes;
+    contours.reserve(ex_polygons.size());
+    holes.reserve(std::accumulate(ex_polygons.begin(), ex_polygons.end(), 0,
+                                  [](size_t sum, const ExPolygon &ex_poly) { return sum + ex_poly.holes.size(); }));
+    for (const ExPolygon &ex_poly : ex_polygons) {
+        contours.emplace_back(ex_poly.contour);
+        append(holes, ex_poly.holes);
+    }
+    return std::make_pair(std::move(contours), std::move(holes));
+}
+
+#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
+static void export_travel_to_svg(const Polygons                                            &boundary,
+                                 const Line                                                &original_travel,
+                                 const Polyline                                            &result_travel,
+                                 const std::vector<AvoidCrossingPerimeters2::Intersection> &intersections,
+                                 const std::string                                         &path)
+{
+    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");
+
+    for (const AvoidCrossingPerimeters2::Intersection &intersection : intersections)
+        svg.draw(intersection.point, "lightseagreen");
+}
+#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
+
 ExPolygons AvoidCrossingPerimeters2::get_boundary(const Layer &layer)
 {
     const coord_t perimeter_spacing = get_perimeter_spacing(layer);
-    const coord_t offset            = perimeter_spacing / 2;
+    const coord_t perimeter_offset  = perimeter_spacing / 2;
     size_t        polygons_count    = 0;
     for (const LayerRegion *layer_region : layer.regions())
         polygons_count += layer_region->slices.surfaces.size();
@@ -269,25 +302,31 @@ ExPolygons AvoidCrossingPerimeters2::get_boundary(const Layer &layer)
         for (const Surface &surface : layer_region->slices.surfaces) boundary.emplace_back(surface.expolygon);
 
     boundary                      = union_ex(boundary);
-    ExPolygons perimeter_boundary = offset_ex(boundary, -offset);
-    ExPolygons final_boundary;
+    ExPolygons perimeter_boundary = offset_ex(boundary, -perimeter_offset);
+    ExPolygons result_boundary;
     if (perimeter_boundary.size() != boundary.size()) {
-        // If any part of the polygon is missing after shrinking, the boundary of slice is used instead.
-        ExPolygons missing_perimeter_boundary = offset_ex(diff_ex(boundary, offset_ex(perimeter_boundary, offset + SCALED_EPSILON / 2)),
-                                                          offset + SCALED_EPSILON);
-        perimeter_boundary                    = offset_ex(perimeter_boundary, offset);
+        // If any part of the polygon is missing after shrinking, then for misisng parts are is used the boundary of the slice.
+        ExPolygons missing_perimeter_boundary = offset_ex(diff_ex(boundary,
+                                                                  offset_ex(perimeter_boundary, perimeter_offset + SCALED_EPSILON / 2)),
+                                                          perimeter_offset + SCALED_EPSILON);
+        perimeter_boundary                    = offset_ex(perimeter_boundary, perimeter_offset);
         perimeter_boundary.reserve(perimeter_boundary.size() + missing_perimeter_boundary.size());
-        perimeter_boundary.insert(perimeter_boundary.begin(), missing_perimeter_boundary.begin(), missing_perimeter_boundary.end());
-        final_boundary = union_ex(intersection_ex(offset_ex(perimeter_boundary, -offset), boundary));
+        perimeter_boundary.insert(perimeter_boundary.end(), missing_perimeter_boundary.begin(), missing_perimeter_boundary.end());
+        // By calling intersection_ex some artifacts arose by previous operations are removed.
+        result_boundary = union_ex(intersection_ex(offset_ex(perimeter_boundary, -perimeter_offset), boundary));
     } else {
-        final_boundary = std::move(perimeter_boundary);
+        result_boundary = std::move(perimeter_boundary);
     }
 
+    auto [contours, holes] = split_expolygon(boundary);
     // Add an outer boundary to avoid crossing perimeters from supports
-    ExPolygons outer_boundary = diff_ex(offset_ex(boundary, 2 * perimeter_spacing), offset_ex(boundary, 2 * perimeter_spacing - offset));
-    final_boundary.reserve(final_boundary.size() + outer_boundary.size());
-    final_boundary.insert(final_boundary.begin(), outer_boundary.begin(), outer_boundary.end());
-    final_boundary = union_ex(final_boundary);
+    ExPolygons outer_boundary = union_ex(
+        diff(static_cast<Polygons>(Geometry::convex_hull(offset(contours, 2 * perimeter_spacing))),
+             offset(contours, perimeter_spacing + perimeter_offset)));
+    result_boundary.insert(result_boundary.end(), outer_boundary.begin(), outer_boundary.end());
+    ExPolygons holes_boundary = offset_ex(holes, -perimeter_spacing);
+    result_boundary.insert(result_boundary.end(), holes_boundary.begin(), holes_boundary.end());
+    result_boundary = union_ex(result_boundary);
 
     // Collect all top layers that will not be crossed.
     polygons_count = 0;
@@ -303,15 +342,18 @@ ExPolygons AvoidCrossingPerimeters2::get_boundary(const Layer &layer)
                 if (surface.is_top()) top_layer_polygons.emplace_back(surface.expolygon);
 
         top_layer_polygons = union_ex(top_layer_polygons);
-        return diff_ex(final_boundary, offset_ex(top_layer_polygons, -offset));
+        return diff_ex(result_boundary, offset_ex(top_layer_polygons, -perimeter_offset));
     }
 
-    return final_boundary;
+    return result_boundary;
 }
 
 ExPolygons AvoidCrossingPerimeters2::get_boundary_external(const Layer &layer)
 {
-    ExPolygons boundary;
+    const coord_t perimeter_spacing = get_perimeter_spacing_external(layer);
+    const coord_t perimeter_offset  = perimeter_spacing / 2;
+    ExPolygons    boundary;
+    // Collect all polygons for all printed objects and their instances, which will be printed at the same time as passed "layer".
     for (const PrintObject *object : layer.object()->print()->objects()) {
         ExPolygons polygons_per_obj;
         for (Layer *l : object->layers())
@@ -327,24 +369,19 @@ ExPolygons AvoidCrossingPerimeters2::get_boundary_external(const Layer &layer)
             for (; boundary_idx < boundary.size(); ++boundary_idx) boundary[boundary_idx].translate(instance.shift.x(), instance.shift.y());
         }
     }
-
-    const coord_t perimeter_spacing = get_perimeter_spacing_external(layer);
-    const coord_t perimeter_offset  = perimeter_spacing / 2;
-
-    Polygons contours;
-    Polygons holes;
-    contours.reserve(boundary.size());
-    for (ExPolygon &poly : boundary) {
-        contours.emplace_back(poly.contour);
-        append(holes, poly.holes);
-    }
-
-    ExPolygons final_boundary = union_ex(diff(offset(contours, perimeter_spacing * 3), offset(contours, 3 * perimeter_spacing - perimeter_offset)));
+    boundary               = union_ex(boundary);
+    auto [contours, holes] = split_expolygon(boundary);
+    // Polygons in which is possible traveling without crossing perimeters of another object.
+    // A convex hull allows removing unnecessary detour caused by following the boundary of the object.
+    ExPolygons result_boundary = union_ex(
+        diff(static_cast<Polygons>(Geometry::convex_hull(offset(contours, 2 * perimeter_spacing))),
+             offset(contours,  perimeter_spacing + perimeter_offset)));
+    // All holes are extended for forcing travel around the outer perimeter of a hole when a hole is crossed.
     ExPolygons holes_boundary = union_ex(diff(offset(holes, perimeter_spacing), offset(holes, perimeter_offset)));
-    final_boundary.reserve(final_boundary.size() + holes_boundary.size());
-    final_boundary.insert(final_boundary.end(), holes_boundary.begin(), holes_boundary.end());
-    final_boundary = union_ex(final_boundary);
-    return final_boundary;
+    result_boundary.reserve(result_boundary.size() + holes_boundary.size());
+    result_boundary.insert(result_boundary.end(), holes_boundary.begin(), holes_boundary.end());
+    result_boundary = union_ex(result_boundary);
+    return result_boundary;
 }
 
 // Returns a direction of the shortest path along the polygon boundary
@@ -409,12 +446,12 @@ Polyline AvoidCrossingPerimeters2::simplify_travel(const EdgeGrid::Grid &edge_gr
         bool                  intersect  = false;
     } visitor(edge_grid);
 
-    Polyline optimized_comb_path;
-    optimized_comb_path.points.reserve(travel.points.size());
-    optimized_comb_path.points.emplace_back(travel.points.front());
+    Polyline simplified_path;
+    simplified_path.points.reserve(travel.points.size());
+    simplified_path.points.emplace_back(travel.points.front());
 
     // Try to skip some points in the path.
-    for (size_t point_idx = 1; point_idx < travel.size(); point_idx++) {
+    for (size_t point_idx = 1; point_idx < travel.size(); ++point_idx) {
         const Point &current_point = travel.points[point_idx - 1];
         Point        next          = travel.points[point_idx];
 
@@ -436,10 +473,10 @@ Polyline AvoidCrossingPerimeters2::simplify_travel(const EdgeGrid::Grid &edge_gr
             }
         }
 
-        optimized_comb_path.append(next);
+        simplified_path.append(next);
     }
 
-    return optimized_comb_path;
+    return simplified_path;
 }
 
 size_t AvoidCrossingPerimeters2::avoid_perimeters(const Polygons       &boundaries,
@@ -502,45 +539,72 @@ size_t AvoidCrossingPerimeters2::avoid_perimeters(const Polygons       &boundari
     Polyline result;
     result.append(start);
     for (auto it_first = intersections.begin(); it_first != intersections.end(); ++it_first) {
+        // The entry point to the boundary polygon
         const Intersection &intersection_first = *it_first;
-        for (auto it_second = it_first + 1; it_second != intersections.end(); ++it_second) {
+        // Skip the it_first from the search for the farthest exit point from the boundary polygon
+        auto it_last_item = std::make_reverse_iterator(it_first) - 1;
+        // Search for the farthest intersection different from it_first but with the same border_idx
+        auto it_second_r  = std::find_if(intersections.rbegin(), it_last_item, [&intersection_first](const Intersection &intersection) {
+            return intersection_first.border_idx == intersection.border_idx;
+        });
+
+        // Append the first 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 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.append(get_middle_point_offset(boundaries[intersection_first.border_idx], left_idx, right_idx, intersection_first.point, SCALED_EPSILON));
+
+        // Check if intersection line also exit the boundary polygon
+        if (it_second_r != it_last_item) {
+            // Transform reverse iterator to forward
+            auto it_second = (it_second_r.base() - 1);
+            // The exit point from the boundary polygon
             const Intersection &intersection_second = *it_second;
-            if (intersection_first.border_idx == intersection_second.border_idx) {
-                Lines border_lines = boundaries[intersection_first.border_idx].lines();
-                // 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));
+            Lines               border_lines        = boundaries[intersection_first.border_idx].lines();
 
-                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
-                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))
-                        result.append(get_polygon_vertex_offset(boundaries[intersection_first.border_idx],
-                                                                (line_idx + 1 == int(boundaries[intersection_first.border_idx].points.size())) ? 0 : (line_idx + 1), SCALED_EPSILON));
-                else
-                    for (int line_idx = intersection_first.line_idx; line_idx != int(intersection_second.line_idx);
-                         line_idx     = (((line_idx - 1) >= 0) ? (line_idx - 1) : (int(border_lines.size()) - 1)))
-                        result.append(get_polygon_vertex_offset(boundaries[intersection_second.border_idx], line_idx + 0, 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
+            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))
+                    result.append(get_polygon_vertex_offset(boundaries[intersection_first.border_idx],
+                                                            (line_idx + 1 == int(boundaries[intersection_first.border_idx].points.size())) ? 0 : (line_idx + 1), SCALED_EPSILON));
+            else
+                for (int line_idx = intersection_first.line_idx; line_idx != int(intersection_second.line_idx);
+                    line_idx      = (((line_idx - 1) >= 0) ? (line_idx - 1) : (int(border_lines.size()) - 1)))
+                    result.append(get_polygon_vertex_offset(boundaries[intersection_second.border_idx], line_idx + 0, SCALED_EPSILON));
 
-                // 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.append(get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, SCALED_EPSILON));
-                // Skip intersections in between
-                it_first = (it_second - 1);
-                break;
-            }
+            // 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.append(get_middle_point_offset(boundaries[intersection_second.border_idx], left_idx, right_idx, intersection_second.point, SCALED_EPSILON));
+            // Skip intersections in between
+            it_first = it_second;
         }
     }
 
     result.append(end);
-    if(!intersections.empty()) {
-        result = simplify_travel(edge_grid, result);
+
+#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
+    {
+        static int iRun = 0;
+        export_travel_to_svg(boundaries, travel_line_orig, result, intersections,
+                             debug_out_path("AvoidCrossingPerimeters-initial-%d.svg", iRun++));
     }
+#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
+
+    if(!intersections.empty())
+        result = simplify_travel(edge_grid, result);
+
+#ifdef AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT
+    {
+        static int iRun = 0;
+        export_travel_to_svg(boundaries, travel_line_orig, result, intersections,
+                             debug_out_path("AvoidCrossingPerimeters-final-%d.svg", iRun++));
+    }
+#endif /* AVOID_CROSSING_PERIMETERS_DEBUG_OUTPUT */
 
     append(result_out->points, result.points);
     return intersections.size();
diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp
index e288a5b06..01f844de4 100644
--- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp
+++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp
@@ -58,7 +58,7 @@ protected:
 
 class AvoidCrossingPerimeters2 : public AvoidCrossingPerimeters
 {
-protected:
+public:
     struct Intersection
     {
         // Index of the polygon containing this point of intersection.