diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp index fab94cd165..3befc9e33b 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "PointUtils.hpp" namespace Slic3r::sla { @@ -142,6 +143,41 @@ public: /// map of connected lines over point line::b static std::map create_line_connection_over_b(const Lines &lines); + /// + /// Comparator to sort points laying on line from point a to point b + /// + struct SortFromAToB + { + std::function compare; + SortFromAToB(const Line &line) + { + Point dir = LineUtils::direction(line); + compare = (PointUtils::is_majorit_x(dir)) ? + ((dir.x() < 0) ? is_x_grater : is_x_smaller) : + ((dir.y() < 0) ? is_y_grater : is_y_smaller); + } + static bool is_x_grater(const Point &left, const Point &right) + { + return left.x() > right.x(); + } + static bool is_x_smaller(const Point &left, const Point &right) + { + return left.x() < right.x(); + } + static bool is_y_grater(const Point &left, const Point &right) + { + return left.y() > right.y(); + } + static bool is_y_smaller(const Point &left, const Point &right) + { + return left.y() < right.y(); + } + bool operator()(const Point &left, const Point &right) + { + return compare(left, right); + } + }; + static void draw(SVG & svg, const Line &line, const char *color = "gray", diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index f29cb4f892..d59f003ff5 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -18,6 +18,10 @@ // comment definition of NDEBUG to enable assert() // #define NDEBUG + +#define SLA_SAMPLING_STORE_FIELD_TO_SVG +#define SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG + #include using namespace Slic3r::sla; @@ -747,6 +751,15 @@ SupportIslandPoints SampleIslandUtils::sample_voronoi_graph( assert(start_node != nullptr); longest_path = VoronoiGraphUtils::create_longest_path(start_node); // longest_path = create_longest_path_recursive(start_node); + +#ifdef SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG + { + static int counter=0; + SVG svg("voronoiGraph"+std::to_string(counter++)+".svg", LineUtils::create_bounding_box(lines)); + LineUtils::draw(svg, lines, "black",0., true); + VoronoiGraphUtils::draw(svg, graph, 1e6, true); + } +#endif // SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG return sample_expath(longest_path, lines, config); } @@ -758,7 +771,8 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( { using VD = Slic3r::Geometry::VoronoiDiagram; - // DTO represents one island change from tiny part to wide part + // DTO represents one island change from wide to tiny part + // it is stored inside map under source line index struct WideTinyChange{ // new coordinate for line.b point Point new_b; @@ -772,11 +786,24 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( , next_new_a(next_new_a) , next_line_index(next_line_index) {} + + // is used only when multi wide tiny change are on same Line + struct SortFromAToB + { + LineUtils::SortFromAToB compare; + SortFromAToB(const Line &line) : compare(line) {} + bool operator()(const WideTinyChange &left, + const WideTinyChange &right) + { + return compare.compare(left.new_b, right.new_b); + } + }; }; + using WideTinyChanges = std::vector; // store shortening of outline segments - // line index, - std::map wide_tiny_changes; + // line index, vector + std::map wide_tiny_changes; const coord_t min_width = config.min_width_for_outline_support; // cut lines at place where neighbor has width = min_width_for_outline_support // neighbor must be in direction from wide part to tiny part of island @@ -788,21 +815,33 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( const VD::edge_type *edge = neighbor->edge; size_t i1 = edge->cell()->source_index(); size_t i2 = edge->twin()->cell()->source_index(); + + auto add = [&](const Point &p1, const Point &p2, size_t i1, size_t i2) { + WideTinyChange change(p1, p2, i2); + auto item = wide_tiny_changes.find(i1); + if (item == wide_tiny_changes.end()) { + wide_tiny_changes[i1] = {change}; + } else { + WideTinyChange::SortFromAToB pred(lines[i1]); + VectorUtils::insert_sorted(item->second, change, pred); + } + }; const Line &l1 = lines[i1]; if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) { // line1 is shorten on side line1.a --> line2 is shorten on side line2.b - assert(wide_tiny_changes.find(i2) == wide_tiny_changes.end()); - wide_tiny_changes.insert({i2, WideTinyChange(p2, p1, i1)}); + add(p2, p1, i2, i1); } else { // line1 is shorten on side line1.b - assert(wide_tiny_changes.find(i1) == wide_tiny_changes.end()); - wide_tiny_changes.insert({i1, WideTinyChange(p1, p2, i2)}); + add(p1, p2, i1, i2); } }; const VoronoiGraph::Node::Neighbor *neighbor = field_start.neighbor; const VoronoiGraph::Node::Neighbor *twin_neighbor = VoronoiGraphUtils::get_twin(neighbor); - - add_wide_tiny_change(twin_neighbor); + // is input wide tiny change + // first input could be from border of Voronoi Graph + if (twin_neighbor->node->neighbors.size() != 1) { + add_wide_tiny_change(twin_neighbor); + } std::set done; done.insert(twin_neighbor->node); std::queue process; @@ -841,14 +880,9 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( } while (node != nullptr); } + // connection of line on island std::map b_connection = LineUtils::create_line_connection_over_b(lines); - // Continue along line indexes create polygon field - - Points points; - points.reserve(field_line_indexes.size()); - std::vector outline_indexes; - outline_indexes.reserve(field_line_indexes.size()); auto inser_point_b = [&](size_t& index, Points& points, std::set& done) { @@ -859,23 +893,60 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( done.insert(index); index = connection_item->second; }; + + Points points; + points.reserve(field_line_indexes.size()); + std::vector outline_indexes; + outline_indexes.reserve(field_line_indexes.size()); size_t input_index = neighbor->edge->cell()->source_index(); size_t outline_index = input_index; std::set done_indexes; + std::vector source_indexes; + size_t source_indexe_for_change = lines.size(); + // Continue along line indexes and create polygon field do { - const auto &change_item = wide_tiny_changes.find(outline_index); - if (change_item != wide_tiny_changes.end()) { - const WideTinyChange &change = change_item->second; - points.push_back(change.new_b); - points.push_back(change.next_new_a); + auto change_item = wide_tiny_changes.find(outline_index); + while(change_item != wide_tiny_changes.end()) { + const WideTinyChanges &changes = change_item->second; + assert(!changes.empty()); + size_t change_index = 0; + if (!points.empty()) { + const Point &last_point = points.back(); + LineUtils::SortFromAToB pred(lines[outline_index]); + bool no_change = false; + while (pred.compare(changes[change_index].new_b, last_point)) { + ++change_index; + if (change_index >= changes.size()) { + no_change = true; + break; + } + } + if (no_change) break; + } + const WideTinyChange& change = changes[change_index]; + // prevent double points + if (points.empty() || !PointUtils::is_equal(points.back(),change.new_b)){ + points.push_back(change.new_b); + source_indexes.push_back(source_indexe_for_change); + } else { + source_indexes.back() = source_indexe_for_change; + } + // prevent double points + if (!PointUtils::is_equal(lines[change.next_line_index].b, change.next_new_a)){ + points.push_back(change.next_new_a); + source_indexes.push_back(change.next_line_index); + } done_indexes.insert(outline_index); outline_index = change.next_line_index; + change_item = wide_tiny_changes.find(outline_index); } inser_point_b(outline_index, points, done_indexes); + source_indexes.push_back(outline_index); } while (outline_index != input_index); Field field; + field.source_indexes = std::move(source_indexes); field.border.contour = Polygon(points); // finding holes if (done_indexes.size() < field_line_indexes.size()) { @@ -890,6 +961,36 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( field.border.holes.emplace_back(hole_points); } } + +#ifdef SLA_SAMPLING_STORE_FIELD_TO_SVG + { + bool draw_border_line_indexes = false; + bool draw_source_line_indexes = true; + bool draw_field_source_indexes = true; + const char *field_color = "red"; + const char *source_line_color = "black"; + const char *border_line_color = "blue"; + + static int counter = 0; + std::string file_name = "field_" + std::to_string(counter++) + ".svg"; + + SVG svg(file_name, LineUtils::create_bounding_box(lines)); + svg.draw(field.border, field_color); + LineUtils::draw(svg,lines, source_line_color,0., draw_source_line_indexes); + Lines border_lines = to_lines(field.border); + LineUtils::draw(svg, border_lines, border_line_color, 0., draw_border_line_indexes); + if (draw_field_source_indexes) + for (auto &line : border_lines) { + size_t index = &line - &border_lines.front(); + // start of holes + if (index >= field.source_indexes.size()) break; + Point middle_point = (line.a + line.b) / 2; + std::string text = std::to_string(field.source_indexes[index]); + svg.draw_text(middle_point, text.c_str(), field_color); + } + } +#endif //SLA_SAMPLING_STORE_FIELD_TO_SVG + return field; } diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp index d498d81511..9794f2bbb7 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -248,8 +248,8 @@ public: // same size as polygon.points.size() // indexes to source island lines - // in case (index == -1) it means fill the gap from tiny part of island - std::vector source_indexes; + // in case (index > lines.size()) it means fill the gap from tiny part of island + std::vector source_indexes; }; diff --git a/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp b/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp index e55184b495..59e3bfd3c1 100644 --- a/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp @@ -123,6 +123,21 @@ public: } } + /// + /// Insert item into sorted vector of items + /// + /// Sorted vector with items + /// Item to insert + /// Predicate for sorting + /// now inserted item + template + static typename std::vector::iterator insert_sorted( + std::vector &data, const T &item, Pred pred) + { + auto iterator = std::upper_bound(data.begin(), data.end(), item, pred); + return data.insert(iterator, item); + } + }; } // namespace Slic3r::sla diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 645da46011..8c68cd0d9b 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -221,7 +221,7 @@ Slic3r::Polygon create_V_shape(double height, double line_width, double angle = ExPolygon create_tiny_wide_test(double wide, double tiny) { double hole_size = wide; - double width = (2 + 1) * wide + 2 * hole_size; + double width = (3 + 1) * wide + 3 * hole_size; double height = wide + 2*tiny + 2*hole_size; auto outline = PolygonUtils::create_rect( width, height); auto hole = PolygonUtils::create_rect(hole_size, hole_size); @@ -230,14 +230,21 @@ ExPolygon create_tiny_wide_test(double wide, double tiny) auto hole3 = hole; // copy auto hole4 = hole; // copy - int hole_move_x = wide / 2 + hole_size / 2; + int hole_move_x = wide + hole_size; int hole_move_y = wide / 2 + hole_size / 2; - hole.translate({hole_move_x, hole_move_y}); - hole2.translate({-hole_move_x, hole_move_y}); - hole3.translate({hole_move_x, -hole_move_y}); - hole4.translate({-hole_move_x, -hole_move_y}); + hole.translate(hole_move_x, hole_move_y); + hole2.translate(-hole_move_x, hole_move_y); + hole3.translate(hole_move_x, -hole_move_y); + hole4.translate(-hole_move_x, -hole_move_y); + + auto hole5 = PolygonUtils::create_circle(hole_size / 2, 16); + hole5.reverse(); + auto hole6 = hole5; // copy + hole5.translate(0, hole_move_y); + hole6.translate(0, -hole_move_y); + ExPolygon result(outline); - result.holes = {hole, hole2, hole3, hole4}; + result.holes = {hole, hole2, hole3, hole4, hole5, hole6}; return result; } @@ -275,6 +282,9 @@ ExPolygons createTestIslands(double size) create_disc(3*size, size / 4, 30), create_square_with_4holes(5 * size, 5 * size / 2 - size / 3), + // Tiny and wide part together with holes + create_tiny_wide_test(3 * size, 2 / 3. * size), + // still problem // three support points ExPolygon(PolygonUtils::create_equilateral_triangle(3 * size)),