From 7a4ba7d131328b43f1db6e55aff317ea0837b510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 21 Sep 2020 02:05:52 +0200 Subject: [PATCH] A simple algorithm to follow the boundary of polygons --- src/libslic3r/GCode.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ src/libslic3r/GCode.hpp | 28 +++++++++++-- 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 61ef9a1bd5..10514ef394 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -176,6 +176,93 @@ namespace Slic3r { return islands; } + Matrix2d rotation_by_direction(const Point &direction) + { + Matrix2d rotation; + rotation.block<1, 2>(0, 0) = direction.cast() / direction.cast().norm(); + rotation(1, 0) = -rotation(0, 1); + rotation(1, 1) = rotation(0, 0); + + return rotation; + } + + Polyline AvoidCrossingPerimeters2::travel_to(const GCode &gcodegen, const Point &point) + { + const Point &start = gcodegen.last_pos(); + const Point &end = point; + const Point direction = end - start; + Matrix2d transform_to_x_axis = rotation_by_direction(direction); + Matrix2d transform_from_x_axis = transform_to_x_axis.transpose(); + + const Line travel_line((transform_to_x_axis * start.cast()).cast(), + (transform_to_x_axis * end.cast()).cast()); + + Polygons borders; + borders.reserve(gcodegen.layer()->lslices.size()); + + for (const ExPolygon &ex_polygon : gcodegen.layer()->lslices) { + borders.emplace_back(ex_polygon.contour); + + for (const Polygon &hole : ex_polygon.holes) borders.emplace_back(hole); + } + + std::vector intersections; + for (size_t border_idx = 0; border_idx < borders.size(); ++border_idx) { + const Polygon &border = borders[border_idx]; + Lines border_lines = border.lines(); + + for (size_t line_idx = 0; line_idx < border_lines.size(); ++line_idx) { + const Line &border_line = border_lines[line_idx]; + Line border_line_transformed((transform_to_x_axis * border_line.a.cast()).cast(), + (transform_to_x_axis * border_line.b.cast()).cast()); + + Point intersection_transformed; + + if (travel_line.intersection(border_line_transformed, &intersection_transformed)) + intersections.emplace_back(border_idx, line_idx, intersection_transformed); + } + } + + // Sort intersections from the nearest to the farthest + std::sort(intersections.begin(), intersections.end()); + + // Polyline result(start, end); + Polyline result; + result.append(start); + + for (auto it_first = intersections.begin(); it_first != intersections.end(); ++it_first) { + const Intersection &intersection_first = *it_first; + + for (auto it_second = it_first + 1; it_second != intersections.end(); ++it_second) { + const Intersection &intersection_second = *it_second; + + if (intersection_first.border_idx == intersection_second.border_idx) { + Lines border_lines = borders[intersection_first.border_idx].lines(); + + // Append the nearest intersection into the path + result.append((transform_from_x_axis * intersection_first.point.cast()).cast()); + + // Append the path around the border into the path + for (size_t line_idx = intersection_first.line_idx; line_idx != intersection_second.line_idx;) { + result.append(border_lines[line_idx].b); + + if (++line_idx == border_lines.size()) line_idx = 0; + } + + // Append the farthest intersection into the path + result.append((transform_from_x_axis * intersection_second.point.cast()).cast()); + + // Skip intersections in between + it_first = it_second; + break; + } + } + } + + result.append(end); + + return result; + } std::string OozePrevention::pre_toolchange(GCode& gcodegen) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index aeac4fcf82..ad9a2e2fb7 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -46,13 +46,13 @@ public: bool disable_once; AvoidCrossingPerimeters() : use_external_mp(false), use_external_mp_once(false), disable_once(true) {} - ~AvoidCrossingPerimeters() {} + virtual ~AvoidCrossingPerimeters() = default; void reset() { 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); } - Polyline travel_to(const GCode &gcodegen, const Point &point); + virtual Polyline travel_to(const GCode &gcodegen, const Point &point); private: // For initializing the regions to avoid. @@ -62,6 +62,28 @@ private: std::unique_ptr m_layer_mp; }; +class AvoidCrossingPerimeters2 : public AvoidCrossingPerimeters +{ +protected: + struct Intersection + { + size_t border_idx; + size_t line_idx; + Point point; + + Intersection(size_t border_idx, size_t line_idx, Point point) + : border_idx(border_idx), line_idx(line_idx), point(point){}; + + inline bool operator<(const Intersection &other) const { return this->point.x() < other.point.x(); } + }; + +public: + AvoidCrossingPerimeters2() : AvoidCrossingPerimeters() {} + + virtual ~AvoidCrossingPerimeters2() = default; + + virtual Polyline travel_to(const GCode &gcodegen, const Point &point) override; +}; class OozePrevention { public: @@ -326,7 +348,7 @@ private: std::set m_placeholder_parser_failed_templates; OozePrevention m_ooze_prevention; Wipe m_wipe; - AvoidCrossingPerimeters m_avoid_crossing_perimeters; + AvoidCrossingPerimeters2 m_avoid_crossing_perimeters; bool m_enable_loop_clipping; // If enabled, the G-code generator will put following comments at the ends // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END