From cf876eeafd8db226effcdf7457eb17774d764b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 2 Sep 2022 19:52:44 +0200 Subject: Fix of #8793: Visible vertical "seam" on fuzzy skin on the place where extrusion begins. --- src/libslic3r/PerimeterGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7fbadc32a..922c0064d 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -119,7 +119,7 @@ static void fuzzy_extrusion_line(Arachne::ExtrusionLine &ext_lines, double fuzzy } if (ext_lines.back().p == ext_lines.front().p) // Connect endpoints. - out.back().p = out.front().p; + out.front().p = out.back().p; if (out.size() >= 3) ext_lines.junctions = std::move(out); -- cgit v1.2.3 From 028c08143e250653d24dabc9747ee6091a112407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 28 Jul 2022 13:42:14 +0200 Subject: Added information that the concentric infill is also affected by the selected perimeter generator. --- src/libslic3r/PrintConfig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d7f3617ac..b1389a953 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -6000,7 +6000,8 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::perimeter; def->tooltip = L("Classic perimeter generator produces perimeters with constant extrusion width and for " "very thin areas is used gap-fill. " - "Arachne engine produces perimeters with variable extrusion width."); + "Arachne engine produces perimeters with variable extrusion width. " + "This setting also affects the Concentric infill."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("classic"); def->enum_values.push_back("arachne"); -- cgit v1.2.3 From 7d6a72f485790b54bfcbc34403c79bb6208e5305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 2 Aug 2022 13:20:54 +0200 Subject: Modified text drawing methods in debugging visualization (SVG) to support changing font size. --- src/libslic3r/SVG.cpp | 24 +++++++++++++----------- src/libslic3r/SVG.hpp | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp index 45eb20d94..377bb450f 100644 --- a/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -308,26 +308,28 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo return d.str(); } -void SVG::draw_text(const Point &pt, const char *text, const char *color) +void SVG::draw_text(const Point &pt, const char *text, const char *color, const coordf_t font_size) { fprintf(this->f, - "%s", - to_svg_x(pt(0)-origin(0)), - to_svg_y(pt(1)-origin(1)), + R"(%s)", + to_svg_x(float(pt.x() - origin.x())), + to_svg_y(float(pt.y() - origin.y())), + font_size, color, text); } -void SVG::draw_legend(const Point &pt, const char *text, const char *color) +void SVG::draw_legend(const Point &pt, const char *text, const char *color, const coordf_t font_size) { fprintf(this->f, - "", - to_svg_x(pt(0)-origin(0)), - to_svg_y(pt(1)-origin(1)), + R"()", + to_svg_x(float(pt.x() - origin.x())), + to_svg_y(float(pt.y() - origin.y())), color); fprintf(this->f, - "%s", - to_svg_x(pt(0)-origin(0)) + 20.f, - to_svg_y(pt(1)-origin(1)), + R"(%s)", + to_svg_x(float(pt.x() - origin.x())) + 20.f, + to_svg_y(float(pt.y() - origin.y())), + font_size, "black", text); } diff --git a/src/libslic3r/SVG.hpp b/src/libslic3r/SVG.hpp index 617cd9679..5e2d92507 100644 --- a/src/libslic3r/SVG.hpp +++ b/src/libslic3r/SVG.hpp @@ -76,8 +76,8 @@ public: void draw(const ClipperLib::Path &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0); void draw(const ClipperLib::Paths &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0); - void draw_text(const Point &pt, const char *text, const char *color); - void draw_legend(const Point &pt, const char *text, const char *color); + void draw_text(const Point &pt, const char *text, const char *color, coordf_t font_size = 20.f); + void draw_legend(const Point &pt, const char *text, const char *color, coordf_t font_size = 10.f); void Close(); -- cgit v1.2.3 From acdb0e849dea363ec7e8f998bb24122d4a492b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 10 Aug 2022 16:25:15 +0200 Subject: Added a lot of debugging outputs (SVG) into SkeletalTrapezoidation. --- src/libslic3r/Arachne/SkeletalTrapezoidation.cpp | 164 +++++++++++++++++++---- src/libslic3r/Arachne/SkeletalTrapezoidation.hpp | 7 + src/libslic3r/PerimeterGenerator.cpp | 35 +++++ src/libslic3r/SVG.cpp | 3 +- 4 files changed, 185 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index eb8894222..fea920130 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -22,8 +22,6 @@ #define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 //A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance). -//#define ARACHNE_DEBUG - namespace boost::polygon { template<> struct geometry_concept @@ -46,6 +44,71 @@ template<> struct segment_traits namespace Slic3r::Arachne { +#ifdef ARACHNE_DEBUG +static void export_graph_to_svg(const std::string &path, + SkeletalTrapezoidationGraph &graph, + const Polygons &polys, + const std::vector> &edge_junctions = {}, + const bool beat_count = true, + const bool transition_middles = true, + const bool transition_ends = true) +{ + const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"}; + coordf_t stroke_width = scale_(0.03); + BoundingBox bbox = get_extents(polys); + for (const auto &node : graph.nodes) + bbox.merge(node.p); + + bbox.offset(scale_(1.)); + + ::Slic3r::SVG svg(path.c_str(), bbox); + for (const auto &line : to_lines(polys)) + svg.draw(line, "gray", stroke_width); + + for (const auto &edge : graph.edges) + svg.draw(Line(edge.from->p, edge.to->p), (edge.data.centralIsSet() && edge.data.isCentral()) ? "blue" : "cyan", stroke_width); + + for (const auto &line_junction : edge_junctions) + for (const auto &extrusion_junction : *line_junction) + svg.draw(extrusion_junction.p, "orange", coord_t(stroke_width * 2.)); + + if (beat_count) { + for (const auto &node : graph.nodes) { + svg.draw(node.p, "red", coord_t(stroke_width * 1.6)); + svg.draw_text(node.p, std::to_string(node.data.bead_count).c_str(), "black", 1); + } + } + + if (transition_middles) { + for (auto &edge : graph.edges) { + if (std::shared_ptr> transitions = edge.data.getTransitions(); transitions) { + for (auto &transition : *transitions) { + Line edge_line = Line(edge.to->p, edge.from->p); + double edge_length = edge_line.length(); + Point pt = edge_line.a + (edge_line.vector().cast() * (double(transition.pos) / edge_length)).cast(); + svg.draw(pt, "magenta", coord_t(stroke_width * 1.5)); + svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1); + } + } + } + } + + if (transition_ends) { + for (auto &edge : graph.edges) { + if (std::shared_ptr> transitions = edge.data.getTransitionEnds(); transitions) { + for (auto &transition : *transitions) { + Line edge_line = Line(edge.to->p, edge.from->p); + double edge_length = edge_line.length(); + Point pt = edge_line.a + (edge_line.vector().cast() * (double(transition.pos) / edge_length)).cast(); + svg.draw(pt, transition.is_lower_end ? "green" : "lime", coord_t(stroke_width * 1.5)); + svg.draw_text(pt, std::to_string(transition.lower_bead_count).c_str(), "black", 1); + } + } + } + } +} +#endif + SkeletalTrapezoidation::node_t& SkeletalTrapezoidation::makeNode(vd_t::vertex_type& vd_node, Point p) { auto he_node_it = vd_node_to_he_node.find(&vd_node); @@ -489,6 +552,10 @@ inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalT void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) { +#ifdef ARACHNE_DEBUG + this->outline = polys; +#endif + // Check self intersections. assert([&polys]() -> bool { EdgeGrid::Grid grid; @@ -517,7 +584,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) Geometry::VoronoiDiagram voronoi_diagram; construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); -#ifdef ARACHNE_DEBUG +#ifdef ARACHNE_DEBUG_VORONOI { static int iRun = 0; dump_voronoi_to_svg(debug_out_path("arachne_voronoi-diagram-%d.svg", iRun++).c_str(), voronoi_diagram, to_points(polys), to_lines(polys)); @@ -694,45 +761,62 @@ void SkeletalTrapezoidation::separatePointyQuadEndNodes() // vvvvvvvvvvvvvvvvvvvvv // -#if 0 -static void export_graph_to_svg(const std::string &path, const SkeletalTrapezoidationGraph &graph, const Polygons &polys) +void SkeletalTrapezoidation::generateToolpaths(std::vector &generated_toolpaths, bool filter_outermost_central_edges) { - const std::vector colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"}; - coordf_t stroke_width = scale_(0.05); - BoundingBox bbox; - for (const auto &node : graph.nodes) - bbox.merge(node.p); - - bbox.offset(scale_(1.)); - ::Slic3r::SVG svg(path.c_str(), bbox); - for (const auto &line : to_lines(polys)) - svg.draw(line, "red", stroke_width); - - for (const auto &edge : graph.edges) - svg.draw(Line(edge.from->p, edge.to->p), "cyan", scale_(0.01)); -} +#ifdef ARACHNE_DEBUG + static int iRun = 0; #endif -void SkeletalTrapezoidation::generateToolpaths(std::vector &generated_toolpaths, bool filter_outermost_central_edges) -{ p_generated_toolpaths = &generated_toolpaths; updateIsCentral(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-updateIsCentral-final-%d.svg", iRun), this->graph, this->outline); +#endif + filterCentral(central_filter_dist); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-filterCentral-final-%d.svg", iRun), this->graph, this->outline); +#endif + if (filter_outermost_central_edges) filterOuterCentral(); updateBeadCount(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-updateBeadCount-final-%d.svg", iRun), this->graph, this->outline); +#endif + filterNoncentralRegions(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-filterNoncentralRegions-final-%d.svg", iRun), this->graph, this->outline); +#endif + generateTransitioningRibs(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-final-%d.svg", iRun), this->graph, this->outline); +#endif + generateExtraRibs(); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateExtraRibs-final-%d.svg", iRun), this->graph, this->outline); +#endif + generateSegments(); + +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-final-%d.svg", iRun), this->graph, this->outline); +#endif + +#ifdef ARACHNE_DEBUG + ++iRun; +#endif } void SkeletalTrapezoidation::updateIsCentral() @@ -944,11 +1028,24 @@ void SkeletalTrapezoidation::generateTransitioningRibs() filterTransitionMids(); +#ifdef ARACHNE_DEBUG + static int iRun = 0; + export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-mids-%d.svg", iRun++), this->graph, this->outline); +#endif + ptr_vector_t> edge_transition_ends; // We only map the half edge in the upward direction. mapped items are not sorted generateAllTransitionEnds(edge_transition_ends); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateTransitioningRibs-ends-%d.svg", iRun++), this->graph, this->outline); +#endif + applyTransitions(edge_transition_ends); // Note that the shared pointer lists will be out of scope and thus destroyed here, since the remaining refs are weak_ptr. + +#ifdef ARACHNE_DEBUG + ++iRun; +#endif } @@ -1670,17 +1767,38 @@ void SkeletalTrapezoidation::generateSegments() } } } - + +#ifdef ARACHNE_DEBUG + static int iRun = 0; + export_graph_to_svg(debug_out_path("ST-generateSegments-before-propagation-%d.svg", iRun), this->graph, this->outline); +#endif + propagateBeadingsUpward(upward_quad_mids, node_beadings); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-upward-propagation-%d.svg", iRun), this->graph, this->outline); +#endif + propagateBeadingsDownward(upward_quad_mids, node_beadings); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-downward-propagation-%d.svg", iRun), this->graph, this->outline); +#endif + ptr_vector_t edge_junctions; // junctions ordered high R to low R generateJunctions(node_beadings, edge_junctions); +#ifdef ARACHNE_DEBUG + export_graph_to_svg(debug_out_path("ST-generateSegments-junctions-%d.svg", iRun), this->graph, this->outline, edge_junctions); +#endif + connectJunctions(edge_junctions); - + generateLocalMaximaSingleBeads(); + +#ifdef ARACHNE_DEBUG + ++iRun; +#endif } SkeletalTrapezoidation::edge_t* SkeletalTrapezoidation::getQuadMaxRedgeTo(edge_t* quad_start_edge) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp index 83065cf87..819b71367 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.hpp @@ -20,6 +20,9 @@ #include "SkeletalTrapezoidationGraph.hpp" #include "../Geometry/Voronoi.hpp" +//#define ARACHNE_DEBUG +//#define ARACHNE_DEBUG_VORONOI + namespace Slic3r::Arachne { @@ -123,6 +126,10 @@ public: */ void generateToolpaths(std::vector &generated_toolpaths, bool filter_outermost_central_edges = false); +#ifdef ARACHNE_DEBUG + Polygons outline; +#endif + protected: /*! * Auxiliary for referencing one transition along an edge which may contain multiple transitions diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 922c0064d..a8b59e9cf 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -29,6 +29,13 @@ #include #include +//#define ARACHNE_DEBUG + +#ifdef ARACHNE_DEBUG +#include "SVG.hpp" +#include "Utils.hpp" +#endif + namespace Slic3r { PerimeterGeneratorLoops get_all_Childs(PerimeterGeneratorLoop loop) { @@ -137,6 +144,27 @@ void convert_to_clipperpath(const Polygons& source, ClipperLib_Z::Paths& dest) { } } +#ifdef ARACHNE_DEBUG +static void export_perimeters_to_svg(const std::string &path, const Polygons &contours, const std::vector &perimeters, const ExPolygons &infill_area) +{ + coordf_t stroke_width = scale_(0.03); + BoundingBox bbox = get_extents(contours); + bbox.offset(scale_(1.)); + ::Slic3r::SVG svg(path.c_str(), bbox); + + svg.draw(infill_area, "cyan"); + + for (const Arachne::VariableWidthLines &perimeter : perimeters) + for (const Arachne::ExtrusionLine &extrusion_line : perimeter) { + ThickPolyline thick_polyline = to_thick_polyline(extrusion_line); + svg.draw({thick_polyline}, "green", "blue", stroke_width); + } + + for (const Line &line : to_lines(contours)) + svg.draw(line, "red", stroke_width); +} +#endif + // Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper // "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling" ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const Surface& surface) { @@ -167,6 +195,13 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const std::vector perimeters = wallToolPaths.getToolPaths(); loop_number = int(perimeters.size()) - 1; +#ifdef ARACHNE_DEBUG + { + static int iRun = 0; + export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour())); + } +#endif + // All closed ExtrusionLine should have the same the first and the last point. // But in rare cases, Arachne produce ExtrusionLine marked as closed but without // equal the first and the last point. diff --git a/src/libslic3r/SVG.cpp b/src/libslic3r/SVG.cpp index 377bb450f..893cbdd95 100644 --- a/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -321,9 +321,10 @@ void SVG::draw_text(const Point &pt, const char *text, const char *color, const void SVG::draw_legend(const Point &pt, const char *text, const char *color, const coordf_t font_size) { fprintf(this->f, - R"()", + R"()", to_svg_x(float(pt.x() - origin.x())), to_svg_y(float(pt.y() - origin.y())), + font_size, color); fprintf(this->f, R"(%s)", -- cgit v1.2.3 From 19988c1a160754c83a2faf074dabe01c0e18dcf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 11 Aug 2022 16:21:57 +0200 Subject: Fix of #8597 - Assert on Windows about decrementing of std::vector::begin() in a specific case. --- src/libslic3r/Arachne/WallToolPaths.cpp | 54 ++---- tests/libslic3r/test_arachne.cpp | 322 ++++++++++++++++++++++++++++++++ 2 files changed, 342 insertions(+), 34 deletions(-) diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index 3d4c4c0ae..27fe9159f 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -332,60 +332,46 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re }; auto new_end = thiss.end(); - if(remove_holes) - { - for(auto it = thiss.begin(); it < new_end; it++) - { - // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector - if(fabs(ClipperLib::Area(to_path(*it))) < min_area_size) - { - new_end--; + if (remove_holes) { + for (auto it = thiss.begin(); it < new_end;) { + // All polygons smaller than target are removed by replacing them with a polygon from the back of the vector. + if (fabs(ClipperLib::Area(to_path(*it))) < min_area_size) { + --new_end; *it = std::move(*new_end); - it--; // wind back the iterator such that the polygon just swaped in is checked next + continue; // Don't increment the iterator such that the polygon just swapped in is checked next. } + ++it; } - } - else - { + } else { // For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes std::vector small_holes; - for(auto it = thiss.begin(); it < new_end; it++) { - double area = ClipperLib::Area(to_path(*it)); - if (fabs(area) < min_area_size) - { - if(area >= 0) - { - new_end--; - if(it < new_end) { + for (auto it = thiss.begin(); it < new_end;) { + if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) { + if (area >= 0) { + --new_end; + if (it < new_end) { std::swap(*new_end, *it); - it--; - } - else - { // Don't self-swap the last Path + continue; + } else { // Don't self-swap the last Path break; } - } - else - { + } else { small_holes.push_back(*it); } } + ++it; } // Removes small holes that have their first point inside one of the removed outlines // Iterating in reverse ensures that unprocessed small holes won't be moved const auto removed_outlines_start = new_end; - for(auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++) - { - for(auto outline_it = removed_outlines_start; outline_it < thiss.end() ; outline_it++) - { - if(Polygon(*outline_it).contains(*hole_it->begin())) { + for (auto hole_it = small_holes.rbegin(); hole_it < small_holes.rend(); hole_it++) + for (auto outline_it = removed_outlines_start; outline_it < thiss.end(); outline_it++) + if (Polygon(*outline_it).contains(*hole_it->begin())) { new_end--; *hole_it = std::move(*new_end); break; } - } - } } thiss.resize(new_end-thiss.begin()); } diff --git a/tests/libslic3r/test_arachne.cpp b/tests/libslic3r/test_arachne.cpp index 991fae00e..c8ea262fe 100644 --- a/tests/libslic3r/test_arachne.cpp +++ b/tests/libslic3r/test_arachne.cpp @@ -102,3 +102,325 @@ TEST_CASE("Arachne - Missing perimeter - #8472", "[ArachneMissingPerimeter8472]" REQUIRE(perimeters.size() == 3); } +// This test case was distilled from GitHub issue #8593. +// Where on the symmetrical model, there were missing parts of extrusions in gear teeth based on model rotation. +TEST_CASE("Arachne - #8593 - Missing a part of the extrusion", "[ArachneMissingPartOfExtrusion8593]") { + const Polygon poly_orig = { + Point( 1800000, 28500000), + Point( 1100000, 30000000), + Point( 1000000, 30900000), + Point( 600000, 32300000), + Point( -600000, 32300000), + Point(-1000000, 30900000), + Point(-1100000, 30000000), + Point(-1800000, 29000000), + }; + + coord_t spacing = 377079; + coord_t inset_count = 3; + + PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); + print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.315, false)); + print_object_config.wall_transition_angle.set(new ConfigOptionFloat(40.)); + print_object_config.wall_transition_length.set(new ConfigOptionFloatOrPercent(1., false)); + + // This behavior seems to be related to the rotation of the input polygon. + // There are specific angles in which this behavior is always triggered. + for (const double angle : {0., -PI / 2., -PI / 15.}) { + Polygon poly = poly_orig; + if (angle != 0.) + poly.rotate(angle); + + Polygons polygons = {poly}; + Arachne::WallToolPaths wall_tool_paths(polygons, spacing, spacing, inset_count, 0, 0.2, print_object_config, PrintConfig::defaults()); + wall_tool_paths.generate(); + std::vector perimeters = wall_tool_paths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + { + static int iRun = 0; + export_perimeters_to_svg(debug_out_path("arachne-missing-part-of-extrusion-8593-%d.svg", iRun++), polygons, perimeters, union_ex(wall_tool_paths.getInnerContour())); + } +#endif + } +} + +// This test case was distilled from GitHub issue #8573. +TEST_CASE("Arachne - #8573 - A gap in the perimeter - 1", "[ArachneGapInPerimeter8573_1]") { + const Polygon poly = { + Point(13960000, 500000), + Point(13920000, 1210000), + Point(13490000, 2270000), + Point(12960000, 3400000), + Point(12470000, 4320000), + Point(12160000, 4630000), + Point(12460000, 3780000), + Point(12700000, 2850000), + Point(12880000, 1910000), + Point(12950000, 1270000), + Point(13000000, 500000), + }; + + Polygons polygons = {poly}; + coord_t spacing = 407079; + coord_t inset_count = 2; + + PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); +// print_object_config.wall_transition_angle.set(new ConfigOptionFloat(20.)); + + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, print_object_config, PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-gap-in-perimeter-1-8573.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif +} + +// This test case was distilled from GitHub issue #8444. +TEST_CASE("Arachne - #8444 - A gap in the perimeter - 2", "[ArachneGapInPerimeter8444_2]") { + const Polygon poly = { + Point(14413938, 3825902), + Point(16817613, 711749), + Point(19653030, 67154), + Point(20075592, 925370), + Point(20245428, 1339788), + Point(20493219, 2121894), + Point(20570295, 2486625), + Point(20616559, 2835232), + Point(20631964, 3166882), + Point(20591800, 3858877), + Point(19928267, 2153012), + Point(19723020, 1829802), + Point(19482017, 1612364), + Point(19344810, 1542433), + Point(19200249, 1500902), + Point(19047680, 1487200), + Point(18631073, 1520777), + Point(18377524, 1567627), + Point(18132517, 1641174), + Point(17896307, 1741360), + Point(17669042, 1868075), + Point(17449999, 2021790), + }; + + Polygons polygons = {poly}; + coord_t spacing = 594159; + coord_t inset_count = 2; + + PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); + // print_object_config.wall_transition_angle.set(new ConfigOptionFloat(20.)); + + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.4, print_object_config, PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-gap-in-perimeter-2-8444.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif +} + +// This test case was distilled from GitHub issue #8528. +// There is a hole in the place where the number of perimeters is changing from 6 perimeters to 7 perimeters. +TEST_CASE("Arachne - #8528 - A hole when number of perimeters is changing", "[ArachneHoleOnPerimetersChange8528]") { + const Polygon poly = { + Point(-30000000, 27650000), + Point(-30000000, 33500000), + Point(-40000000, 33500000), + Point(-40500000, 33500000), + Point(-41100000, 33400000), + Point(-41600000, 33200000), + Point(-42100000, 32900000), + Point(-42600000, 32600000), + Point(-43000000, 32200000), + Point(-43300000, 31700000), + Point(-43600000, 31200000), + Point(-43800000, 30700000), + Point(-43900000, 30100000), + Point(-43900000, 29600000), + Point(-43957080, 25000000), + Point(-39042920, 25000000), + Point(-39042920, 27650000), + }; + + Polygons polygons = {poly}; + coord_t spacing = 814159; + coord_t inset_count = 5; + + PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); + print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.68, false)); + + // Changing min_bead_width to 0.66 seems that resolve this issue, at least in this case. + print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.66, false)); + + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.4, print_object_config, PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-hole-on-perimeters-change-8528.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif +} + +// This test case was distilled from GitHub issue #8528. +// There is an inconsistency between layers in length of the single perimeters. +TEST_CASE("Arachne - #8555 - Inconsistent single perimeter", "[ArachneInconsistentSinglePerimeter8555]") { + const Polygon poly_0 = { + Point(5527411, -38490007), + Point(11118814, -36631169), + Point(13529600, -36167120), + Point(11300145, -36114514), + Point(10484024, -36113916), + Point(5037323, -37985945), + Point(4097054, -39978866) + }; + const Polygon poly_1 = { + Point(5566841, -38517205), + Point(11185208, -36649404), + Point(13462719, -36211009), + Point(11357290, -36161329), + Point(10583855, -36160763), + Point(5105952, -38043516), + Point(4222019, -39917031) + }; + const Polygon poly_2 = { + Point(5606269, -38544404), + Point(11251599, -36667638), + Point(13391666, -36255700), + Point(10683552, -36207653), + Point(5174580, -38101085), + Point(4346981, -39855197) + }; + const Polygon poly_3 = { + Point(5645699, -38571603), + Point(11317993, -36685873), + Point(13324786, -36299588), + Point(10783383, -36254499), + Point(5243209, -38158655), + Point(4471947, -39793362) + }; + const Polygon poly_4 = { + Point(5685128, -38598801), + Point(11384385, -36704108), + Point(13257907, -36343476), + Point(10883211, -36301345), + Point(5311836, -38216224), + Point(4596909, -39731528) + }; + const Polygon poly_5 = { + Point(5724558, -38626000), + Point(11450778, -36722343), + Point(13191026, -36387365), + Point(10983042, -36348191), + Point(5380466, -38273795), + Point(4721874, -39669693) + }; + + Polygons polygons = {poly_0, poly_1, poly_2, poly_3, poly_4, poly_5}; + coord_t spacing = 417809; + coord_t inset_count = 2; + + for (size_t poly_idx = 0; poly_idx < polygons.size(); ++poly_idx) { + Polygons input_polygons{polygons[poly_idx]}; + Arachne::WallToolPaths wallToolPaths(input_polygons, spacing, spacing, inset_count, 0, 0.15, PrintObjectConfig::defaults(), PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-inconsistent-single-perimeter-8555-%d.svg", poly_idx), input_polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif + } +} + +// This test case was distilled from GitHub issue #8633. +// Open perimeter extrusion is shorter on endpoints in comparison to closed perimeter. +TEST_CASE("Arachne - #8633 - Shorter open perimeter", "[ArachneShorterOpenPerimeter8633]") { + const Polygon poly_0 = { + Point(6507498, 4189461), + Point(6460382, 3601960), + Point(6390896, 3181097), + Point(6294072, 2765838), + Point(6170293, 2357794), + + Point(7090581, 2045388), + Point(7232821, 2514293), + Point(7344089, 2991501), + Point(7423910, 3474969), + Point(7471937, 3962592), + Point(7487443, 4436235), + Point(6515575, 4436235), + }; + + const Polygon poly_1 = { + Point(6507498, 4189461), + Point(6460382, 3601960), + Point(6390896, 3181097), + Point(6294072, 2765838), + Point(6170293, 2357794), + + Point(6917958, 1586830), + Point(7090552, 2045398), + + Point(7232821, 2514293), + Point(7344089, 2991501), + Point(7423910, 3474969), + Point(7471937, 3962592), + Point(7487443, 4436235), + Point(6515575, 4436235), + }; + + Polygons polygons = {poly_0, poly_1}; + coord_t spacing = 617809; + coord_t inset_count = 1; + + PrintObjectConfig print_object_config = PrintObjectConfig::defaults(); + print_object_config.min_bead_width.set(new ConfigOptionFloatOrPercent(0.51, false)); + print_object_config.min_feature_size.set(new ConfigOptionFloatOrPercent(0.15, false)); + print_object_config.wall_transition_length.set(new ConfigOptionFloatOrPercent(0.6, false)); + + for (size_t poly_idx = 0; poly_idx < polygons.size(); ++poly_idx) { + Polygons input_polygons{polygons[poly_idx]}; + Arachne::WallToolPaths wallToolPaths(input_polygons, spacing, spacing, inset_count, 0, 0.15, print_object_config, PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-shorter-open-perimeter-8633-%d.svg", poly_idx), input_polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif + } +} + +// This test case was distilled from GitHub issue #8597. +// There was just an issue with decrementing std::vector::begin() in a specific case. +TEST_CASE("Arachne - #8597 - removeSmallAreas", "[ArachneRemoveSmallAreas8597]") { + const Polygon poly_0 = { + Point(-38768167, -3636556), + Point(-38763631, -3617883), + Point(-38763925, -3617820), + Point(-38990169, -3919539), + Point(-38928506, -3919539), + }; + + const Polygon poly_1 = { + Point(-39521732, -4480560), + Point(-39383333, -4398498), + Point(-39119825, -3925307), + Point(-39165608, -3926212), + Point(-39302205, -3959445), + Point(-39578719, -4537002), + }; + + Polygons polygons = {poly_0, poly_1}; + coord_t spacing = 407079; + coord_t inset_count = 2; + + Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults()); + wallToolPaths.generate(); + std::vector perimeters = wallToolPaths.getToolPaths(); + +#ifdef ARACHNE_DEBUG_OUT + export_perimeters_to_svg(debug_out_path("arachne-remove-small-areas-8597.svg"), polygons, perimeters, union_ex(wallToolPaths.getInnerContour())); +#endif + + REQUIRE(perimeters.size() == 1); +} \ No newline at end of file -- cgit v1.2.3 From 2ca5f1b274d2fb1271b92d4fb6c6627c79b17b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 11 Aug 2022 22:44:05 +0200 Subject: Reconnected endpoints of closed polyline when PolylineStitcher produced an open polyline mark as closed. --- src/libslic3r/Arachne/WallToolPaths.cpp | 8 ++++++++ tests/libslic3r/test_arachne.cpp | 31 ++++++++++--------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index 27fe9159f..556747b14 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -615,6 +615,14 @@ void WallToolPaths::stitchToolPaths(std::vector &toolpaths, { continue; } + + // PolylineStitcher, in some cases, produced closed extrusion (polygons), + // but the endpoints differ by a small distance. So we reconnect them. + // FIXME Lukas H.: Investigate more deeply why it is happening. + if (wall_polygon.junctions.front().p != wall_polygon.junctions.back().p && + (wall_polygon.junctions.back().p - wall_polygon.junctions.front().p).cast().norm() < stitch_distance) { + wall_polygon.junctions.emplace_back(wall_polygon.junctions.front()); + } wall_polygon.is_closed = true; wall_lines.push_back(std::move(wall_polygon)); // add stitched polygons to result } diff --git a/tests/libslic3r/test_arachne.cpp b/tests/libslic3r/test_arachne.cpp index c8ea262fe..7ef5eda14 100644 --- a/tests/libslic3r/test_arachne.cpp +++ b/tests/libslic3r/test_arachne.cpp @@ -33,30 +33,19 @@ static void draw_extrusion(const std::string &path, const Polygons &polygons, co TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") { Polygon poly = { - Point(62478540, -7411873), Point(62478540, 9978540), Point(-62478540, 9978540), Point(-62478540, -7411873), Point(-58818049, -7411874), - Point(-58639424, -7393054), Point(-58430204, -7325743), Point(-58317958, -7261069), Point(-58187096, -7150294), Point(-58032997, -6934055), - Point(-57956770, -6723830), Point(-57927922, -6536131), Point(-57948215, -6249353), Point(-58038066, -5971432), Point(-58400146, -5419566), - Point(-58720844, -4711417), Point(-58812247, -4429032), Point(-58945877, -3868129), Point(-58999054, -3458536), Point(-59021495, -3000104), - Point(-58978088, -2345755), Point(-58945641, -2130557), Point(-58719408, -1284462), Point(-58350699, -492550), Point(-57854519, 218384), - Point(-57690839, 403070), Point(-57242241, 834472), Point(-56937894, 1068372), Point(-56522699, 1341801), Point(-56245930, 1488656), - Point(-55633586, 1748152), Point(-54872819, 1945077), Point(-54279560, 2011550), Point(-53999789, 2021482), Point(-53550613, 1995780), - Point(-53127364, 1945077), Point(-52852060, 1886312), Point(-52262142, 1711199), Point(-51479386, 1343079), Point(-50763215, 839141), - Point(-50302640, 384003), Point(-50150730, 224721), Point(-49616021, -551391), Point(-49279548, -1287513), Point(-49210805, -1492871), - Point(-49054178, -2131559), Point(-49004097, -2510916), Point(-48978506, -2999863), Point(-49012690, -3563440), Point(-49054263, -3868963), - Point(-49280289, -4714703), Point(-49649307, -5507428), Point(-49921685, -5909300), Point(-49982145, -6037568), Point(-50058406, -6311890), - Point(-50071436, -6450122), Point(-50045218, -6724784), Point(-50006267, -6852153), Point(-49876677, -7082551), Point(-49687434, -7260679), - Point(-49451449, -7373245), Point(-49184639, -7411873), Point(-13258854, -7411871), Point(-13258853, -7021460), Point(-44501672, -7021460), - Point(-44816622, -6971390), Point(-45100558, -6826592), Point(-45326629, -6600516), Point(-45471725, -6315721), Point(-45521460, -6001689), - Point(-45521460, 3000836), Point(-45509082, 3159381), Point(-45412385, 3459576), Point(-45326445, 3600421), Point(-45221777, 3722975), - Point(-44964550, 3909861), Point(-44815338, 3971666), Point(-44598502, 4016264), Point(58501687, 4021460), Point(58814884, 3971856), - Point(58964551, 3909863), Point(59221777, 3722976), Point(59326668, 3600164), Point(59436707, 3406438), Point(59508907, 3160338), - Point(59521460, 3000842), Point(59521460, -6001688), Point(59471724, -6315713), Point(59326597, -6600557), Point(59100555, -6826595), - Point(58814822, -6971976), Point(58501662, -7021460), Point(27258850, -7021460), Point(27258851, -7411871), Point(59755385, -7411874), + Point(-40000000, 10000000), + Point(-62480000, 10000000), + Point(-62480000, -7410000), + Point(-58430000, -7330000), + Point(-58400000, -5420000), + Point(-58720000, -4710000), + Point(-58940000, -3870000), + Point(-59020000, -3000000), }; Polygons polygons = {poly}; coord_t spacing = 407079; - coord_t inset_count = 8; + coord_t inset_count = 5; Arachne::WallToolPaths wallToolPaths(polygons, spacing, spacing, inset_count, 0, 0.2, PrintObjectConfig::defaults(), PrintConfig::defaults()); wallToolPaths.generate(); @@ -69,7 +58,7 @@ TEST_CASE("Arachne - Closed ExtrusionLine", "[ArachneClosedExtrusionLine]") { for (VariableWidthLines &perimeter : perimeters) for (ExtrusionLine &el : perimeter) if (el.is_closed) { -// REQUIRE(el.junctions.front().p == el.junctions.back().p); + REQUIRE(el.junctions.front().p == el.junctions.back().p); } } -- cgit v1.2.3 From f4c3a20dee7d2f10b5898a8c40f81bbe0b27ceb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 12 Aug 2022 09:17:11 +0200 Subject: Follow-up to 0161a59a93e326d5e573cffe61b9549a4d925189: Too big epsilon created tiny unconnected extrusions, so epsilon was changed to the smallest value that allows resolving rounding issues after division by two. --- src/libslic3r/Arachne/SkeletalTrapezoidation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index fea920130..7bd37df11 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -2031,10 +2031,10 @@ void SkeletalTrapezoidation::generateJunctions(ptr_vector_t& for (junction_idx = (std::max(size_t(1), beading->toolpath_locations.size()) - 1) / 2; junction_idx < num_junctions; junction_idx--) { coord_t bead_R = beading->toolpath_locations[junction_idx]; - // toolpath_locations computed inside DistributedBeadingStrategy be off by 1 because of rounding errors. + // toolpath_locations computed inside DistributedBeadingStrategy could be off by 1 because of rounding errors. // In GH issue #8472, these roundings errors caused missing the middle extrusion. - // Adding some epsilon should help resolve those cases. - if (bead_R <= start_R + scaled(0.005)) + // Adding small epsilon should help resolve those cases. + if (bead_R <= start_R + 1) { // Junction coinciding with start node is used in this function call break; } -- cgit v1.2.3 From e39c9b01e98c268e24ab8e787d68d6f8003e76ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 17 Aug 2022 15:55:02 +0200 Subject: Fixed incorrectly computed starting position in Concentric infill when Arachne is used. --- src/libslic3r/Fill/FillConcentric.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 7f0bfa002..26c57c071 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -267,6 +267,7 @@ void FillConcentric::_fill_surface_single(const FillParams ¶ms, assert(thick_polyline.points.size() == thick_polyline.points_width.size()); } thick_polylines_out.emplace_back(std::move(thick_polyline)); + last_pos = thick_polylines_out.back().last_point(); } // clip the paths to prevent the extruder from getting exactly on the first point of the loop -- cgit v1.2.3 From 0f0b4d1c200cff25c751c91ba33e897c71c65eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Fri, 22 Jul 2022 11:50:58 +0200 Subject: Fixed an issue that the wipe was shorter when it was located where extrusion width was changing. (Heavily modified by supermerill for superslicer) --- src/libslic3r/ExtrusionEntity.hpp | 7 +++++ src/libslic3r/Fill/FillBase.cpp | 4 +-- src/libslic3r/GCode.cpp | 48 ++++++++++++++++++++++++-------- src/libslic3r/GCode.hpp | 2 ++ src/libslic3r/Geometry/MedialAxis.cpp | 39 +++++++++++++------------- src/libslic3r/Geometry/MedialAxis.hpp | 4 ++- src/libslic3r/PerimeterGenerator.cpp | 52 ++++++++++++++++++++++++++--------- 7 files changed, 110 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 20a50502a..abd357821 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -216,6 +216,7 @@ public: static ExtrusionRole string_to_role(const std::string_view role); }; +//FIXME: don't use that unsafe container. use a vector of sharedpointer or a ExtrusionEntityCollection. typedef std::vector ExtrusionEntitiesPtr; class ExtrusionPath : public ExtrusionEntity @@ -409,6 +410,9 @@ public: ExtrusionMultiPath(const ExtrusionPaths &paths) : ExtrusionMultiEntity(paths) {}; ExtrusionMultiPath(const ExtrusionPath &path) :ExtrusionMultiEntity(path) {} + ExtrusionMultiPath& operator=(const ExtrusionMultiPath& rhs) { this->paths = rhs.paths; return *this; } + ExtrusionMultiPath& operator=(ExtrusionMultiPath&& rhs) { this->paths = std::move(rhs.paths); return *this; } + virtual ExtrusionMultiPath* clone() const override { return new ExtrusionMultiPath(*this); } virtual ExtrusionMultiPath* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); } @@ -425,6 +429,9 @@ public: ExtrusionMultiPath3D(const ExtrusionPaths3D &paths) : ExtrusionMultiEntity(paths) {}; ExtrusionMultiPath3D(const ExtrusionPath3D &path) :ExtrusionMultiEntity(path) {} + ExtrusionMultiPath3D& operator=(const ExtrusionMultiPath3D& rhs) { this->paths = rhs.paths; return *this; } + ExtrusionMultiPath3D& operator=(ExtrusionMultiPath3D&& rhs) { this->paths = std::move(rhs.paths); return *this; } + virtual ExtrusionMultiPath3D* clone() const override { return new ExtrusionMultiPath3D(*this); } virtual ExtrusionMultiPath3D* clone_move() override { return new ExtrusionMultiPath3D(std::move(*this)); } diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 8fa8e4772..d9789bd5b 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -217,8 +217,8 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ for (const ExtrusionEntity* entity : entities) { extruded_volume += entity->total_volume(); } - //append - all_new_paths->append(entities); + //append (move so the pointers are reused, and won't need to be deleted) + all_new_paths->append(std::move(entities)); } thick_polylines.clear(); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ac8f8b732..8c97c5f7c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3855,7 +3855,13 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s const coordf_t full_loop_length = loop_to_seam.length(); const bool is_full_loop_ccw = loop_to_seam.polygon().is_counter_clockwise(); //after that point, loop_to_seam can be modified by 'paths', so don't use it anymore - +#if _DEBUG + for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) { + assert(it->polyline.points.size() >= 2); + assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + } + assert(loop.paths.front().first_point() == loop.paths.back().last_point()); +#endif // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case @@ -3978,8 +3984,8 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // reset acceleration m_writer.set_acceleration((uint16_t)floor(get_default_acceleration(m_config) + 0.5)); - if (m_wipe.enable) - m_wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path + //basic wipe, may be erased after if we need a more complex one + add_wipe_points(paths); //wipe for External Perimeter (and not vase) if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 0 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 2 @@ -4159,16 +4165,39 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s return gcode; } +template +void GCode::add_wipe_points(const std::vector& paths) { + if (m_wipe.enable) { + m_wipe.path = std::move(paths.back().polyline); + m_wipe.path.reverse(); + + for (auto it = std::next(paths.rbegin()); it != paths.rend(); ++it) { + if (is_bridge(it->role())) + break; // Do not perform a wipe on bridges. + + assert(it->polyline.points.size() >= 2); + assert(m_wipe.path.points.back() == it->polyline.last_point()); + if (m_wipe.path.points.back() != it->polyline.last_point()) + break; // ExtrusionMultiPath is interrupted in some place. + + m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.rbegin() + 1, it->polyline.points.rend()); + } + } +} + std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, const std::string &description, double speed) { +#if _DEBUG + for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) { + assert(it->polyline.points.size() >= 2); + assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + } +#endif // extrude along the path std::string gcode; for (const ExtrusionPath &path : multipath.paths) { gcode += extrude_path(path, description, speed); } - if (m_wipe.enable) { - m_wipe.path = std::move(multipath.paths.back().polyline); // TODO: don't limit wipe to last path - m_wipe.path.reverse(); - } + add_wipe_points(multipath.paths); // reset acceleration m_writer.set_acceleration((uint16_t)floor(get_default_acceleration(m_config) + 0.5)); return gcode; @@ -4202,10 +4231,7 @@ std::string GCode::extrude_multi_path3D(const ExtrusionMultiPath3D &multipath3D, } gcode += this->_after_extrude(path); } - if (m_wipe.enable) { - m_wipe.path = std::move(multipath3D.paths.back().polyline); // TODO: don't limit wipe to last path - m_wipe.path.reverse(); - } + add_wipe_points(multipath3D.paths); // reset acceleration m_writer.set_acceleration((uint16_t)floor(get_default_acceleration(m_config) + 0.5)); return gcode; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index a2555ed77..369b96d36 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -302,6 +302,8 @@ private: std::string extrude_path(const ExtrusionPath &path, const std::string &description, double speed = -1.); std::string extrude_path_3D(const ExtrusionPath3D &path, const std::string &description, double speed = -1.); void split_at_seam_pos(ExtrusionLoop &loop, std::unique_ptr *lower_layer_edge_grid, bool was_clockwise); + template // can be templated safely because private + void add_wipe_points(const std::vector& paths); // Extruding multiple objects with soluble / non-soluble / combined supports // on a multi-material printer, trying to minimize tool switches. diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp index 16a710a48..5c758c627 100644 --- a/src/libslic3r/Geometry/MedialAxis.cpp +++ b/src/libslic3r/Geometry/MedialAxis.cpp @@ -3043,8 +3043,11 @@ MedialAxis::build(ThickPolylines& polylines_out) } +ExtrusionMultiPath variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance) { + return ExtrusionMultiPath(unsafe_variable_width(polyline, role, flow, resolution_internal, tolerance)); +} ExtrusionPaths -variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance) +unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance) { ExtrusionPaths paths; @@ -3189,7 +3192,7 @@ variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Fl path.polyline.append(line.b); } else { // we need to initialize a new line - paths.emplace_back(std::move(path)); + paths.push_back(path); path = ExtrusionPath(role); if (wanted_width != current_flow.width()) { current_flow = current_flow.with_width(wanted_width); @@ -3207,7 +3210,7 @@ variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Fl assert(path.polyline.points.size() > 2 || path.first_point() != path.last_point()); } if (path.polyline.is_valid()) - paths.emplace_back(std::move(path)); + paths.push_back(path); return paths; } @@ -3223,36 +3226,34 @@ ExtrusionEntitiesPtr const coord_t tolerance = flow.scaled_width() / 10;//scale_(0.05); ExtrusionEntitiesPtr coll; for (const ThickPolyline& p : polylines) { - - ExtrusionPaths paths = variable_width(p, role, flow, resolution_internal, tolerance); + ExtrusionMultiPath multi_paths = variable_width(p, role, flow, resolution_internal, tolerance); // Append paths to collection. - if (!paths.empty()) { - for (auto it = std::next(paths.begin()); it != paths.end(); ++it) { + if (!multi_paths.empty()) { +#if _DEBUG + for (auto it = std::next(multi_paths.paths.begin()); it != multi_paths.paths.end(); ++it) { assert(it->polyline.points.size() >= 2); assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } - if (paths.front().first_point().coincides_with_epsilon(paths.back().last_point())) { - coll.push_back(new ExtrusionLoop(std::move(paths))); +#endif + if (multi_paths.paths.front().first_point().coincides_with_epsilon(multi_paths.paths.back().last_point())) { + coll.push_back(new ExtrusionLoop(std::move(multi_paths.paths))); } else { if (role == erThinWall) { //thin walls : avoid to cut them, please. //also, keep the start, as the start should be already in a frontier where possible. - ExtrusionEntityCollection* unsortable_coll = new ExtrusionEntityCollection(std::move(paths)); + ExtrusionEntityCollection* unsortable_coll = new ExtrusionEntityCollection(std::move(multi_paths.paths)); unsortable_coll->set_can_sort_reverse(false, false); + //TODO un-reversable multipath ? coll.push_back(unsortable_coll); } else if (role == erGapFill) { - if (paths.size() == 1) { - coll.push_back(paths.front().clone_move()); + if (multi_paths.size() == 1) { + coll.push_back(multi_paths.paths.front().clone_move()); } else { - ExtrusionEntityCollection* unsortable_coll = new ExtrusionEntityCollection(std::move(paths)); - //gap fill : can reverse, but refrain from cutting them as it creates a mess. - // I say that, but currently (false, true) does bad things. - unsortable_coll->set_can_sort_reverse(false, true); - coll.push_back(unsortable_coll); + //can reverse but not sort/cut: it's a multipath! + coll.push_back(multi_paths.clone_move()); } } else { - for (ExtrusionPath& path : paths) - coll.push_back(new ExtrusionPath(std::move(path))); + coll.push_back(multi_paths.clone_move()); } } } diff --git a/src/libslic3r/Geometry/MedialAxis.hpp b/src/libslic3r/Geometry/MedialAxis.hpp index 852c0161c..c010bc14f 100644 --- a/src/libslic3r/Geometry/MedialAxis.hpp +++ b/src/libslic3r/Geometry/MedialAxis.hpp @@ -159,7 +159,9 @@ private: /// create a ExtrusionEntitiesPtr from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed. Please delete all ptr if not used. ExtrusionEntitiesPtr thin_variable_width(const ThickPolylines& polylines, const ExtrusionRole role, const Flow &flow, const coord_t resolution_internal); // used by thin_variable_width. Only does the work for a single polyline. -ExtrusionPaths variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance); +ExtrusionMultiPath variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance); +//prefer using multi_variable_width +ExtrusionPaths unsafe_variable_width(const ThickPolyline& polyline, const ExtrusionRole role, const Flow& flow, const coord_t resolution_internal, const coord_t tolerance); } } // namespace Slicer::Geometry diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index a8b59e9cf..47f2923f0 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1942,7 +1942,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon return paths; } - +//TODO: transform to ExtrusionMultiPath instead of ExtrusionPaths ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& arachne_path, ExtrusionRole role, bool is_external) const { ExtrusionPaths paths; const bool is_loop = Point{ arachne_path.front().x(), arachne_path.front().y() }.coincides_with_epsilon(Point{ arachne_path.back().x(), arachne_path.back().y() }); @@ -1953,7 +1953,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar //assert(path.mm3_per_mm == path.mm3_per_mm); //assert(path.width == path.width); //assert(path.height == path.height); - append(paths, Geometry::variable_width(Arachne::to_thick_polyline(arachne_path), + append(paths, Geometry::unsafe_variable_width(Arachne::to_thick_polyline(arachne_path), role, is_external ? this->ext_perimeter_flow : this->perimeter_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2055,14 +2055,14 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar if (!ok_polylines.empty()) { //fast track if (small_speed.empty() && big_speed.empty() && small_flow.empty() && big_flow.empty()) { - return Geometry::variable_width(Arachne::to_thick_polyline(arachne_path), + return Geometry::unsafe_variable_width(Arachne::to_thick_polyline(arachne_path), role, is_external ? this->ext_perimeter_flow : this->perimeter_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), (is_external ? this->ext_perimeter_flow : this->perimeter_flow).scaled_width() / 10); } for (const ClipperLib_Z::Path& extrusion_path : ok_polylines) { - for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path), + for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path), role, is_external ? this->ext_perimeter_flow : this->perimeter_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2074,7 +2074,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar } if (!small_speed.empty()) { for (const ClipperLib_Z::Path& extrusion_path : small_speed) { - for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path), + for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path), erOverhangPerimeter, is_external ? this->ext_perimeter_flow : this->perimeter_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2086,7 +2086,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar } if (!big_speed.empty()) { for (const ClipperLib_Z::Path& extrusion_path : big_speed) { - for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path), + for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path), erOverhangPerimeter, is_external ? this->ext_perimeter_flow : this->perimeter_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2098,7 +2098,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar } if (!small_flow.empty()) { for (const ClipperLib_Z::Path& extrusion_path : small_flow) { - for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path), + for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path), erOverhangPerimeter, this->overhang_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2110,7 +2110,7 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar } if (!big_flow.empty()) { for (const ClipperLib_Z::Path& extrusion_path : big_flow) { - for (auto&& path : Geometry::variable_width(Arachne::to_thick_polyline(extrusion_path), + for (auto&& path : Geometry::unsafe_variable_width(Arachne::to_thick_polyline(extrusion_path), erOverhangPerimeter, this->overhang_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2575,7 +2575,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_extrusions(std::vector

ext_perimeter_flow : this->perimeter_flow, std::max(this->ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), @@ -2600,11 +2600,37 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_extrusions(std::vector

polyline.points.size() >= 2); + assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + } + assert(extrusion_loop.paths.front().first_point() == extrusion_loop.paths.back().last_point()); +#endif extrusion_coll.append(std::move(extrusion_loop)); - } else - for (ExtrusionPath& path : paths) - extrusion_coll.append(ExtrusionPath(std::move(path))); + } else { + + // Because we are processing one ExtrusionLine all ExtrusionPaths should form one connected path. + // But there is possibility that due to numerical issue there is poss + assert([&paths = std::as_const(paths)]() -> bool { + for (auto it = std::next(paths.begin()); it != paths.end(); ++it) + if (std::prev(it)->polyline.last_point() != it->polyline.first_point()) + return false; + return true; + }()); + ExtrusionMultiPath multi_path; + multi_path.paths.emplace_back(std::move(paths.front())); + + for (auto it_path = std::next(paths.begin()); it_path != paths.end(); ++it_path) { + if (multi_path.paths.back().last_point() != it_path->first_point()) { + extrusion_coll.append(std::move(multi_path)); + multi_path = ExtrusionMultiPath(); + } + multi_path.paths.push_back(std::move(*it_path)); + } + + extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path))); + } } } -- cgit v1.2.3 From d81e39185acb8f66ae4ab546a1d46625cb96eb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 29 Aug 2022 15:58:57 +0200 Subject: Fix of #8724, #8747, and #8753: Crash when Voronoi vertexes of a finite edge have some coordinate NaN or infinite. --- src/libslic3r/Arachne/SkeletalTrapezoidation.cpp | 18 +++++++++++++++++- src/libslic3r/Arachne/utils/VoronoiUtils.cpp | 1 + src/libslic3r/Arachne/utils/VoronoiUtils.hpp | 5 +++++ src/libslic3r/Geometry/VoronoiUtilsCgal.cpp | 7 +++++-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index 7bd37df11..e9afa0305 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -451,8 +451,23 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead constructFromPolygons(polys); } +static bool has_finite_edge_with_non_finite_vertex(const Geometry::VoronoiDiagram &voronoi_diagram) +{ + for (const VoronoiUtils::vd_t::edge_type &edge : voronoi_diagram.edges()) { + if (edge.is_finite()) { + assert(edge.vertex0() != nullptr && edge.vertex1() != nullptr); + if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !VoronoiUtils::is_finite(*edge.vertex0()) || + !VoronoiUtils::is_finite(*edge.vertex1())) + return true; + } + } + return false; +} static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector &segments) { + if (has_finite_edge_with_non_finite_vertex(voronoi_diagram)) + return true; + for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) { if (!cell.incident_edge()) continue; // There is no spoon @@ -471,7 +486,8 @@ static bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &vorono VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr; VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge(); do { - if (edge->is_infinite()) continue; + if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !VoronoiUtils::is_finite(*edge->vertex0()) || !VoronoiUtils::is_finite(*edge->vertex1())) + continue; Vec2i64 v0 = VoronoiUtils::p(edge->vertex0()); Vec2i64 v1 = VoronoiUtils::p(edge->vertex1()); diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp index 3da556b47..82bd79523 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.cpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.cpp @@ -15,6 +15,7 @@ Vec2i64 VoronoiUtils::p(const vd_t::vertex_type *node) { const double x = node->x(); const double y = node->y(); + assert(std::isfinite(x) && std::isfinite(y)); assert(x <= double(std::numeric_limits::max()) && x >= std::numeric_limits::lowest()); assert(y <= double(std::numeric_limits::max()) && y >= std::numeric_limits::lowest()); return {int64_t(x + 0.5 - (x < 0)), int64_t(y + 0.5 - (y < 0))}; // Round to the nearest integer coordinates. diff --git a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp index e736f98bc..aa4693643 100644 --- a/src/libslic3r/Arachne/utils/VoronoiUtils.hpp +++ b/src/libslic3r/Arachne/utils/VoronoiUtils.hpp @@ -35,6 +35,11 @@ public: * The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola. */ static std::vector discretizeParabola(const Point &source_point, const Segment &source_segment, Point start, Point end, coord_t approximate_step_size, float transitioning_angle); + + static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex) + { + return std::isfinite(vertex.x()) && std::isfinite(vertex.y()); + } }; } // namespace Slic3r::Arachne diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp index caaf1ee9c..062a3b397 100644 --- a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp +++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp @@ -3,6 +3,7 @@ #include #include "libslic3r/Geometry/Voronoi.hpp" +#include "libslic3r/Arachne/utils/VoronoiUtils.hpp" #include "VoronoiUtilsCgal.hpp" @@ -28,7 +29,8 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_intersection(const VD &voronoi_ if (edge.color() != 0) continue; - if (edge.is_finite() && edge.is_linear()) { + if (edge.is_finite() && edge.is_linear() && edge.vertex0() != nullptr && edge.vertex1() != nullptr && + Arachne::VoronoiUtils::is_finite(*edge.vertex0()) && Arachne::VoronoiUtils::is_finite(*edge.vertex1())) { segments.emplace_back(to_cgal_point(*edge.vertex0()), to_cgal_point(*edge.vertex1())); edge.color(1); assert(edge.twin() != nullptr); @@ -73,7 +75,8 @@ bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VoronoiDiagram &vor do { // FIXME Lukas H.: Also process parabolic segments. - if (edge->is_finite() && edge->is_linear()) + if (edge->is_finite() && edge->is_linear() && edge->vertex0() != nullptr && edge->vertex1() != nullptr && + Arachne::VoronoiUtils::is_finite(*edge->vertex0()) && Arachne::VoronoiUtils::is_finite(*edge->vertex1())) edges.emplace_back(edge); edge = edge->rot_next(); -- cgit v1.2.3