diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index fbd3379d5..03677b5db 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) -{ - 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)); -} -#endif - void SkeletalTrapezoidation::generateToolpaths(std::vector &generated_toolpaths, bool filter_outermost_central_edges) { +#ifdef ARACHNE_DEBUG + static int iRun = 0; +#endif + 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 } @@ -1668,17 +1765,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 dafa850cd..830d48571 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -12,6 +12,13 @@ #include #include +//#define ARACHNE_DEBUG + +#ifdef ARACHNE_DEBUG +#include "SVG.hpp" +#include "Utils.hpp" +#endif + namespace Slic3r { ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance, const float merge_tolerance) @@ -537,6 +544,27 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator &p return extrusion_coll; } +#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" void PerimeterGenerator::process_arachne() @@ -578,6 +606,13 @@ void PerimeterGenerator::process_arachne() 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 4928d5433..d68301e74 100644 --- a/src/libslic3r/SVG.cpp +++ b/src/libslic3r/SVG.cpp @@ -287,9 +287,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)",