From 51ce8fbd62d758b07e416431ec1bc8a8d5c450d7 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 6 Apr 2021 09:07:43 +0200 Subject: [PATCH] =?UTF-8?q?=EF=BB=BFCreate=20ExPolygon=20from=20wide=20par?= =?UTF-8?q?t=20of=20island?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SLA/SupportIslands/LineUtils.cpp | 127 ++++++++- .../SLA/SupportIslands/LineUtils.hpp | 43 +++ .../SLA/SupportIslands/PointUtils.cpp | 24 ++ .../SLA/SupportIslands/PointUtils.hpp | 33 +++ .../SLA/SupportIslands/SampleConfig.hpp | 30 +- .../SupportIslands/SampleConfigFactory.hpp | 37 ++- .../SLA/SupportIslands/SampleIslandUtils.cpp | 257 +++++++++++++++++- .../SLA/SupportIslands/SampleIslandUtils.hpp | 100 ++++++- .../SLA/SupportIslands/SupportIslandPoint.hpp | 6 +- .../SLA/SupportIslands/VoronoiGraphUtils.cpp | 108 +++++++- .../SLA/SupportIslands/VoronoiGraphUtils.hpp | 132 +++++++-- tests/sla_print/sla_supptgen_tests.cpp | 12 +- 12 files changed, 824 insertions(+), 85 deletions(-) diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp index e081ddbaac..8b056e9eb5 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp @@ -2,6 +2,7 @@ #include #include #include "VectorUtils.hpp" +#include "PointUtils.hpp" using namespace Slic3r::sla; @@ -94,7 +95,7 @@ std::optional LineUtils::crop_half_ray(const Line & half_ray, fnc use_point_y = [&half_ray, &dir](const Point &p) -> bool { return (p.y() > half_ray.a.y()) == (dir.y() > 0); }; - bool use_x = abs(dir.x()) > abs(dir.y()); + bool use_x = PointUtils::is_majorit_x(dir); fnc use_point = (use_x) ? use_point_x : use_point_y; bool use_a = use_point(segment->a); bool use_b = use_point(segment->b); @@ -117,7 +118,7 @@ std::optional LineUtils::crop_half_ray(const Linef & half_ray, fnc use_point_y = [&half_ray, &dir](const Vec2d &p) -> bool { return (p.y() > half_ray.a.y()) == (dir.y() > 0); }; - bool use_x = fabs(dir.x()) > fabs(dir.y()); + bool use_x = PointUtils::is_majorit_x(dir); fnc use_point = (use_x) ? use_point_x : use_point_y; bool use_a = use_point(segment->a); bool use_b = use_point(segment->b); @@ -143,7 +144,7 @@ std::optional LineUtils::crop_line(const Line & line, return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) : (p.y() < line.a.y()) && (p.y() > line.b.y()); }; - bool use_x = abs(dir.x()) > abs(dir.y()); + bool use_x = PointUtils::is_majorit_x(dir); fnc use_point = (use_x) ? use_point_x : use_point_y; bool use_a = use_point(segment->a); bool use_b = use_point(segment->b); @@ -182,7 +183,7 @@ std::optional LineUtils::crop_line(const Linef & line, return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) : (p.y() < line.a.y()) && (p.y() > line.b.y()); }; - bool use_x = abs(dir.x()) > abs(dir.y()); + bool use_x = PointUtils::is_majorit_x(dir); fnc use_point = (use_x) ? use_point_x : use_point_y; bool use_a = use_point(segment->a); bool use_b = use_point(segment->b); @@ -254,6 +255,124 @@ double LineUtils::perp_distance(const Linef &line, Vec2d p) return std::abs(cross2(v, va)) / v.norm(); } +bool LineUtils::is_parallel(const Line &first, const Line &second) +{ + Point dir1 = first.b - first.a; + Point dir2 = second.b - second.a; + coord_t cross( + static_cast(dir1.x()) * dir2.y() - + static_cast(dir2.x()) * dir1.y() + ); + return (cross == 0); +} + +std::optional LineUtils::intersection(const Line &ray1, const Line &ray2) +{ + const Vec2d v1 = direction(ray1).cast(); + const Vec2d v2 = direction(ray2).cast(); + double denom = cross2(v1, v2); + if (fabs(denom) < std::numeric_limits::epsilon()) return {}; + + const Vec2d v12 = (ray1.a - ray2.a).cast(); + double nume = cross2(v2, v12); + double t = nume / denom; + return (ray1.a.cast() + t * v1); +} + +LineUtils::LineConnection LineUtils::create_line_connection(const Slic3r::Lines &lines) +{ + LineConnection line_connection; + static const size_t bad_index = -1; + auto insert = [&](size_t line_index, size_t connected, bool connect_by_a){ + auto item = line_connection.find(line_index); + if (item == line_connection.end()) { + // create new + line_connection[line_index] = (connect_by_a) ? + std::pair(connected, bad_index) : + std::pair(bad_index, connected); + } else { + std::pair &pair = item->second; + size_t &ref_index = (connect_by_a) ? pair.first : pair.second; + assert(ref_index == bad_index); + ref_index = connected; + } + }; + + auto inserts = [&](size_t i1, size_t i2)->bool{ + bool is_l1_a_connect = true; // false => l1_b_connect + const Slic3r::Line &l1 = lines[i1]; + const Slic3r::Line &l2 = lines[i2]; + if (!PointUtils::is_equal(l1.a, l2.b)) return false; + if (!PointUtils::is_equal(l1.b, l2.a)) return false; + else is_l1_a_connect = false; + insert(i1, i2, is_l1_a_connect); + insert(i2, i1, !is_l1_a_connect); + return true; + }; + + std::vector not_finished; + size_t prev_index = lines.size() - 1; + for (size_t index = 0; index < lines.size(); ++index) { + if (!inserts(prev_index, index)) { + bool found_index = false; + bool found_prev_index = false; + std::remove_if(not_finished.begin(), not_finished.end(), + [&](const size_t ¬_finished_index) { + if (!found_index && inserts(index, not_finished_index)) { + found_index = true; + return true; + } + if (!found_prev_index && inserts(prev_index, not_finished_index)) { + found_prev_index = true; + return true; + } + return false; + }); + if (!found_index) not_finished.push_back(index); + if (!found_prev_index) not_finished.push_back(prev_index); + } + prev_index = index; + } + assert(not_finished.empty()); + return line_connection; +} + +std::map LineUtils::create_line_connection_over_b(const Lines &lines) +{ + static const size_t bad_index = -1; + std::map line_connection; + auto inserts = [&](size_t i1, size_t i2) -> bool { + const Line &l1 = lines[i1]; + const Line &l2 = lines[i2]; + bool is_connected = PointUtils::is_equal(l1.b, l2.a); + if (!PointUtils::is_equal(l1.b, l2.a)) + return false; + assert(line_connection.find(i1) == line_connection.end()); + line_connection[i1] = i2; + return true; + }; + + std::vector not_finished; + size_t prev_index = lines.size() - 1; + for (size_t index = 0; index < lines.size(); ++index) { + if (!inserts(prev_index, index)) { + bool found = false; + std::remove_if(not_finished.begin(), not_finished.end(), + [&](const size_t ¬_finished_index) { + if (!found && inserts(prev_index, not_finished_index)) { + found = true; + return true; + } + return false; + }); + if(!found) not_finished.push_back(prev_index); + } + prev_index = index; + } + assert(not_finished.empty()); + return line_connection; +} + void LineUtils::draw(SVG & svg, const Lines &lines, const char * color, diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp index 6b2f7a8780..b84c9cbbdb 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -92,6 +93,48 @@ public: /// Euclid perpedicular distance static double perp_distance(const Linef &line, Vec2d p); + /// + /// Create cross product of line direction. + /// When zero than they are parallel. + /// + /// First line + /// Second line + /// True when direction are same(scaled or oposit or combination) otherwise FALSE + static bool is_parallel(const Line &first, const Line &second); + + /// + /// Intersection of line - no matter on line limitation + /// + /// first line + /// second line + /// intersection point when exist + static std::optional intersection(const Line &ray1, const Line &ray2); + + /// + /// Create direction from point a to point b + /// Direction vector is represented as point + /// + /// input line + /// line direction + static Point direction(const Line &line) { return line.b - line.a; } + + // line index, + using LineConnection = std::map>; + /// + /// Create data structure from exPolygon lines to find if two lines are connected + /// !! not tested + /// + /// Lines created from ExPolygon + /// map of connected lines. + static LineConnection create_line_connection(const Lines &lines); + + /// + /// Create data structure from exPolygon lines to store connected line indexes + /// + /// Lines created from ExPolygon + /// map of connected lines over point line::b + static std::map create_line_connection_over_b(const Lines &lines); + static void draw(SVG & svg, const Line &line, const char *color = "gray", diff --git a/src/libslic3r/SLA/SupportIslands/PointUtils.cpp b/src/libslic3r/SLA/SupportIslands/PointUtils.cpp index e15524384d..b49a3bc859 100644 --- a/src/libslic3r/SLA/SupportIslands/PointUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/PointUtils.cpp @@ -2,3 +2,27 @@ using namespace Slic3r::sla; +bool PointUtils::is_equal(const Point &p1, const Point &p2) { + return p1.x() == p2.x() && p1.y() == p2.y(); +} + +bool PointUtils::is_majorit_x(const Point &point) +{ + return abs(point.x()) > abs(point.y()); +} + +bool PointUtils::is_majorit_x(const Vec2d &point) +{ + return fabs(point.x()) > fabs(point.y()); +} + +Slic3r::Point PointUtils::perp(const Point &vector) +{ + return Point(-vector.x(), vector.y()); +} + +bool PointUtils::is_same_direction(const Point &dir1, const Point &dir2) +{ + return (is_majorit_x(dir1)) ? (dir1.x() > 0) == (dir2.x() > 0) : + (dir1.y() > 0) == (dir2.y() > 0); +} diff --git a/src/libslic3r/SLA/SupportIslands/PointUtils.hpp b/src/libslic3r/SLA/SupportIslands/PointUtils.hpp index 21861f3034..549d9ddea6 100644 --- a/src/libslic3r/SLA/SupportIslands/PointUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/PointUtils.hpp @@ -14,6 +14,39 @@ class PointUtils { public: PointUtils() = delete; + /// + /// Is equal p1 == p2 + /// + /// first point + /// second point + /// True when equal otherwise FALSE + static bool is_equal(const Point &p1, const Point &p2); + + /// + /// check if absolut value of x is grater than absolut value of y + /// + /// input direction + /// True when mayorit vaule is X other wise FALSE(mayorit value is y) + static bool is_majorit_x(const Point &point); + static bool is_majorit_x(const Vec2d &point); + + /// + /// Create perpendicular vector + /// + /// input vector from zero to point coordinate + /// Perpendicular vector + static Point perp(const Point &vector); + + /// + /// Check if both direction are on same half of the circle + /// alpha = atan2 of direction1 + /// beta = atan2 of direction2 + /// beta is in range from(alpha - Pi/2) to (alpha + Pi/2) + /// + /// first direction + /// second direction + /// True when on same half otherwise false + static bool is_same_direction(const Point &dir1, const Point &dir2); }; } // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp index 5c99e802ba..657f3d49f7 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_SLA_SuppotstIslands_SampleConfig_hpp_ #define slic3r_SLA_SuppotstIslands_SampleConfig_hpp_ +#include + namespace Slic3r::sla { /// /// Configuration DTO @@ -11,37 +13,45 @@ struct SampleConfig { // Every point on island has at least one support point in maximum distance // MUST be bigger than zero - double max_distance = 1.; + coord_t max_distance = 2; + coord_t half_distance = 1; // has to be half od max_distance // Support point head radius // MUST be bigger than zero - double head_radius = 1; + coord_t head_radius = 1; // [nano meter] // When it is possible, there will be this minimal distance from outline. // zero when head should be on outline - double minimal_distance_from_outline = 0.; + coord_t minimal_distance_from_outline = 0; // [nano meter] + + // Distinguish when to add support point on VD outline point(center line sample) + // MUST be bigger than minimal_distance_from_outline + coord_t minimal_support_distance = 0; // Maximal length of longest path in voronoi diagram to be island // supported only by one single support point this point will be in center of path. - double max_length_for_one_support_point = 1.; + coord_t max_length_for_one_support_point = 1.; // Maximal length of island supported by 2 points - double max_length_for_two_support_points = 1.; + coord_t max_length_for_two_support_points = 1.; // Maximal width of line island supported in the middle of line - double max_width_for_center_supportr_line = 1.; - // Maximal width of line island supported by zig zag - double max_width_for_zig_zag_supportr_line = 1.; + // Must be greater or equal to min_width_for_outline_support + coord_t max_width_for_center_support_line = 1.; + + // Minimal width to be supported by outline + // Must be smaller or equal to max_width_for_center_support_line + coord_t min_width_for_outline_support = 1.; // Term criteria for end of alignment // Minimal change in manhatn move of support position before termination coord_t minimal_move = 1000; // in nanometers, devide from print resolution to quater pixel // Maximal count of align iteration - size_t count_iteration = 100; + size_t count_iteration = 100; // Maximal distance over Voronoi diagram edges to find closest point during aligning Support point - double max_align_distance = 0; + coord_t max_align_distance = 0.; }; } // namespace Slic3r::sla #endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp index 63ed0e4c6c..f32c32089f 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp @@ -18,20 +18,23 @@ public: { // TODO: find valid params !!!! SampleConfig result; - result.max_distance = 100. * config.head_diameter; + result.max_distance = 100 * config.head_diameter; + result.half_distance = result.max_distance / 2; result.head_radius = config.head_diameter / 2; - result.minimal_distance_from_outline = config.head_diameter / 2.; + result.minimal_distance_from_outline = config.head_diameter / 2.; + result.minimal_support_distance = result.minimal_distance_from_outline + + result.half_distance; result.max_length_for_one_support_point = 2 * result.minimal_distance_from_outline + config.head_diameter; - double max_length_for_one_support_point = + coord_t max_length_for_one_support_point = 2 * result.max_distance + config.head_diameter + 2 * result.minimal_distance_from_outline; if (result.max_length_for_one_support_point > max_length_for_one_support_point) result.max_length_for_one_support_point = max_length_for_one_support_point; - double min_length_for_one_support_point = + coord_t min_length_for_one_support_point = 2 * config.head_diameter + 2 * result.minimal_distance_from_outline; if (result.max_length_for_one_support_point < min_length_for_one_support_point) @@ -40,7 +43,7 @@ public: result.max_length_for_two_support_points = 2 * result.max_distance + 2 * config.head_diameter + 2 * result.minimal_distance_from_outline; - double max_length_for_two_support_points = + coord_t max_length_for_two_support_points = 2 * result.max_distance + 2 * config.head_diameter + 2 * result.minimal_distance_from_outline; @@ -48,23 +51,17 @@ public: result.max_length_for_two_support_points = max_length_for_two_support_points; assert(result.max_length_for_two_support_points < result.max_length_for_one_support_point); - result.max_width_for_center_supportr_line = 2 * config.head_diameter; - double min_width_for_center_supportr_line = + result.max_width_for_center_support_line = 2 * config.head_diameter; + coord_t min_width_for_center_support_line = config.head_diameter + 2 * result.minimal_distance_from_outline; - if (result.max_width_for_center_supportr_line < min_width_for_center_supportr_line) - result.max_width_for_center_supportr_line = min_width_for_center_supportr_line; - double max_width_for_center_supportr_line = 2 * result.max_distance + config.head_diameter; - if (result.max_width_for_center_supportr_line > max_width_for_center_supportr_line) - result.max_width_for_center_supportr_line = max_width_for_center_supportr_line; + if (result.max_width_for_center_support_line < min_width_for_center_support_line) + result.max_width_for_center_support_line = min_width_for_center_support_line; + coord_t max_width_for_center_support_line = 2 * result.max_distance + config.head_diameter; + if (result.max_width_for_center_support_line > max_width_for_center_support_line) + result.max_width_for_center_support_line = max_width_for_center_support_line; - result.max_width_for_zig_zag_supportr_line = sqrt(2*result.max_distance * result.max_distance); - double max_width_for_zig_zag_supportr_line = - 2 * result.max_distance + - 2 * config.head_diameter + - 2 * result.minimal_distance_from_outline; - if (result.max_width_for_zig_zag_supportr_line > max_width_for_zig_zag_supportr_line) - result.max_width_for_zig_zag_supportr_line = max_width_for_zig_zag_supportr_line; - assert(result.max_width_for_zig_zag_supportr_line < result.max_width_for_center_supportr_line); + result.min_width_for_outline_support = result.max_width_for_center_support_line - 2 * config.head_diameter; + assert(result.min_width_for_outline_support >= result.max_width_for_center_support_line); // Align support points // TODO: propagate print resolution diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index 84ce4d022c..d6daf970f7 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -1,12 +1,15 @@ #include "SampleIslandUtils.hpp" #include +#include #include #include "IStackFunction.hpp" #include "EvaluateNeighbor.hpp" #include "ParabolaUtils.hpp" #include "VoronoiGraphUtils.hpp" #include "VectorUtils.hpp" +#include "LineUtils.hpp" +#include "PointUtils.hpp" #include #include @@ -596,7 +599,9 @@ SupportIslandPoints SampleIslandUtils::sample_center_circle( } SupportIslandPoints SampleIslandUtils::sample_expath( - const VoronoiGraph::ExPath &path, const SampleConfig &config) + const VoronoiGraph::ExPath &path, + const Lines & lines, + const SampleConfig & config) { // 1) One support point if (path.length < config.max_length_for_one_support_point) { @@ -608,7 +613,7 @@ SupportIslandPoints SampleIslandUtils::sample_expath( } double max_width = VoronoiGraphUtils::get_max_width(path); - if (max_width < config.max_width_for_center_supportr_line) { + if (max_width < config.max_width_for_center_support_line) { // 2) Two support points if (path.length < config.max_length_for_two_support_points) return create_side_points(path.nodes, @@ -625,24 +630,114 @@ SupportIslandPoints SampleIslandUtils::sample_expath( return std::move(samples); } - // line of zig zag points - if (max_width < config.max_width_for_zig_zag_supportr_line) { - // return create_zig_zag_points(); - } - // TODO: 3) Triangle of points // eval outline and find three point create almost equilateral triangle - // TODO: divide path to sampled parts + // IMPROVE: Erase continous sampling: Extract path and than sample uniformly whole path + CenterStarts center_starts; + const VoronoiGraph::Node *start_node = path.nodes.front(); + // CHECK> Front of path is outline node + assert(start_node->neighbors.size() == 1); + const VoronoiGraph::Node::Neighbor *neighbor = &start_node->neighbors.front(); + center_starts.emplace(neighbor, config.minimal_distance_from_outline); - SupportIslandPoints points; - points.emplace_back(create_point_on_path(path.nodes, config.minimal_distance_from_outline)); + std::set done; // already done nodes + done.insert(start_node); + SupportIslandPoints points; // result + + while (!center_starts.empty()) { + FieldStart field_start; + std::vector new_starts = sample(center_starts.front(), + config, done, points, + field_start); + center_starts.pop(); + for (const CenterStart &start : new_starts) center_starts.push(start); + if (field_start.neighbor != nullptr) { + // TODO: create field + auto field = create_field(field_start, center_starts, lines, config); + } + } + // Fix first point type + if (!points.empty()) { + auto &front = points.front(); + if (front->type == SupportIslandPoint::Type::center_line) + front->type = SupportIslandPoint::Type::center_line_start; + } + + // TODO: remove next line, only for debug + points.push_back(create_point_on_path(path.nodes, config.minimal_distance_from_outline)); return std::move(points); } +std::vector SampleIslandUtils::sample( + const CenterStart & start, + const SampleConfig & config, + std::set &done, + SupportIslandPoints & results, + FieldStart & field_start) +{ + const VoronoiGraph::Node::Neighbor *neighbor = start.neighbor; + const VoronoiGraph::Node *node = neighbor->node; + if (done.find(node) != done.end()) return {}; + done.insert(node); + + VoronoiGraph::Nodes path = start.path; + std::vector new_starts; + double support_in = start.support_in; + while (neighbor->max_width <= config.max_width_for_center_support_line) { + double edge_length = neighbor->edge_length; + while (edge_length > support_in) { + double ratio = support_in / edge_length; + results.push_back( + create_point(neighbor, ratio, + SupportIslandPoint::Type::center_line)); + support_in += config.max_distance; + } + if (support_in > edge_length) + support_in -= edge_length; + + const VoronoiGraph::Node *node = neighbor->node; + path.push_back(node); + const VoronoiGraph::Node::Neighbor *next_neighbor = nullptr; + for (const auto &node_neighbor : node->neighbors) { + if (done.find(node_neighbor.node) != done.end()) continue; + if (next_neighbor == nullptr) { + next_neighbor = &node_neighbor; + continue; + } + double next_support_in = (support_in < config.half_distance) ? + support_in : config.max_distance - support_in; + new_starts.emplace_back(&node_neighbor, next_support_in, path); // search in side branch + } + if (next_neighbor == nullptr) { + // no neighbor to continue + if ((config.max_distance - support_in) >= config.minimal_support_distance) { + VoronoiGraph::Nodes path_reverse = path; // copy + std::reverse(path_reverse.begin(), path_reverse.end()); + results.push_back(create_point_on_path( + path_reverse, config.minimal_distance_from_outline, + SupportCenterIslandPoint::Type::center_line_end3)); + } + + if (new_starts.empty()) return {}; + const CenterStart &new_start = new_starts.back(); + neighbor = new_start.neighbor; + support_in = new_start.support_in; + path = new_start.path; + new_starts.pop_back(); + } else { + neighbor = next_neighbor; + } + } + field_start.neighbor = neighbor; + field_start.last_support = config.max_distance - support_in; + return new_starts; +} + SupportIslandPoints SampleIslandUtils::sample_voronoi_graph( const VoronoiGraph & graph, + const Lines & lines, const SampleConfig & config, VoronoiGraph::ExPath &longest_path) { @@ -652,7 +747,147 @@ 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); - return sample_expath(longest_path, config); + return sample_expath(longest_path, lines, config); +} + +SampleIslandUtils::Field SampleIslandUtils::create_field( + const FieldStart &field_start, + CenterStarts & tiny_starts, + const Lines & lines, + const SampleConfig &config) +{ + using VD = Slic3r::Geometry::VoronoiDiagram; + + // DTO represents one island change from tiny part to wide part + struct WideTinyChange{ + // new coordinate for line.b point + Point new_b; + // new coordinate for next line.a point + Point next_new_a; + // index to lines + size_t next_line_index; + + WideTinyChange(Point new_b, Point next_new_a, size_t next_line_index) + : new_b(new_b) + , next_new_a(next_new_a) + , next_line_index(next_line_index) + {} + }; + + // store shortening of outline segments + // line index, + 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 + auto add_wide_tiny_change = [&](const VoronoiGraph::Node::Neighbor *neighbor){ + VoronoiGraph::Position position = + VoronoiGraphUtils::get_position_with_distance(neighbor, min_width, lines); + const VD::edge_type *edge = neighbor->edge; + size_t i1 = edge->cell()->source_index(); + size_t i2 = edge->twin()->cell()->source_index(); + const Line &l1 = lines[i1]; + const Line &l2 = lines[i2]; + Point p1, p2; + std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, l1, l2); + if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) { + // line1 is shorten on side line1.a --> line2 is shorten on side line2.b + wide_tiny_changes.insert({i2, WideTinyChange(p2, p1, i1)}); + } else { + // line1 is shorten on side line1.b + wide_tiny_changes.insert({i1, WideTinyChange(p1, p2, 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); + std::set done; + done.insert(twin_neighbor->node); + std::queue process; + process.push(neighbor->node); + + // all lines belongs to polygon + std::set field_line_indexes; + while (!process.empty()) { + const VoronoiGraph::Node *node = process.front(); + process.pop(); + if (done.find(node) != done.end()) continue; + do { + done.insert(node); + const auto &neighbors = node->neighbors; + node = nullptr; + for (const VoronoiGraph::Node::Neighbor &neighbor : neighbors) { + if (done.find(neighbor.node) != done.end()) continue; + const VD::edge_type *edge = neighbor.edge; + size_t index1 = edge->cell()->source_index(); + size_t index2 = edge->twin()->cell()->source_index(); + field_line_indexes.insert(index1); + field_line_indexes.insert(index2); + if (neighbor.max_width < + config.min_width_for_outline_support) { + add_wide_tiny_change(&neighbor); + continue; + } + if (node == nullptr) { + node = neighbor.node; + } else { + process.push(neighbor.node); + } + } + } while (node != nullptr); + } + + 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) + { + const Line &line = lines[index]; + points.push_back(line.b); + const auto &connection_item = b_connection.find(index); + assert(connection_item != b_connection.end()); + done.insert(index); + index = connection_item->second; + }; + + size_t input_index = neighbor->edge->cell()->source_index(); + size_t outline_index = input_index; + std::set done_indexes; + 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); + done_indexes.insert(outline_index); + outline_index = change.next_line_index; + } + inser_point_b(outline_index, points, done_indexes); + } while (outline_index != input_index); + + Field field; + field.border.contour = Polygon(points); + // finding holes + if (done_indexes.size() < field_line_indexes.size()) { + for (const size_t &index : field_line_indexes) { + if(done_indexes.find(index) != done_indexes.end()) continue; + // new hole + Points hole_points; + size_t hole_index = index; + do { + inser_point_b(hole_index, hole_points, done_indexes); + } while (hole_index != index); + field.border.holes.emplace_back(hole_points); + } + } + return field; } void SampleIslandUtils::draw(SVG & svg, diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp index 5404748638..d498d81511 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ #define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ +#include + #include #include #include @@ -109,23 +111,25 @@ public: /// Decide how to sample path /// /// Path inside voronoi diagram with side branches and circles + /// Source lines for VG --> outline of island. /// Definition how to sample /// Support points for island - static SupportIslandPoints sample_expath( - const VoronoiGraph::ExPath &path, - const SampleConfig &config - ); + static SupportIslandPoints sample_expath(const VoronoiGraph::ExPath &path, + const Lines & lines, + const SampleConfig &config); /// /// Sample voronoi skeleton /// /// Inside skeleton of island + /// Source lines for VG --> outline of island. /// Params for sampling /// OUTPUT: longest path in graph /// Vector of sampled points or Empty when distance from edge is /// bigger than max_distance static SupportIslandPoints sample_voronoi_graph( const VoronoiGraph & graph, + const Lines & lines, const SampleConfig & config, VoronoiGraph::ExPath &longest_path); @@ -174,6 +178,94 @@ public: double size, const char *color = "lightgreen", bool write_type = true); + + /// + /// DTO hold information for start sampling VD in center + /// + struct CenterStart + { + // Start of ceneter sampled line segment + const VoronoiGraph::Node::Neighbor *neighbor; + + // distance to nearest support point + coord_t support_in; // [nano meters] + + VoronoiGraph::Nodes path; // to sample in distance from border + + CenterStart(const VoronoiGraph::Node::Neighbor *neighbor, + coord_t support_in, + VoronoiGraph::Nodes path = {}) + : neighbor(neighbor), support_in(support_in), path(path) + {} + }; + using CenterStarts = std::queue; + + /// + /// DTO extend VG neighbor with information about last sample + /// + struct FieldStart + { + // define edge where start field + const VoronoiGraph::Node::Neighbor *neighbor = nullptr; + + // distance to last support + // when last support lay on neighbor edge it is negative value + coord_t last_support = 0; // [nano meters] + + FieldStart() = default; + FieldStart(const VoronoiGraph::Node::Neighbor *neighbor, + coord_t last_support) + : neighbor(neighbor), last_support(last_support) + {} + }; + + /// + /// Sample VG in center. + /// Detection of wide neighbor which start field + /// Creating of new starts (from VG cross -> multi neighbors) + /// + /// Start to sample + /// Parameters for sampling + /// Already done nodes + /// Result of sampling + /// Output: Wide neighbor, start of field + /// New start of sampling + static std::vector sample( + const CenterStart & start, + const SampleConfig & config, + std::set &done, + SupportIslandPoints & results, + FieldStart& field_start); + + /// + /// DTO represents area to sample + /// extend polygon with information about source lines + /// + struct Field + { + // border of field created by source lines and closing of tiny island + ExPolygon border; + + // 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; + }; + + + /// + /// Create field from input neighbor + /// + /// Start neighbor, first occur of wide neighbor. + /// Append new founded tiny parts of island. + /// Source lines for VG --> outline of island. + /// Parameters for sampling. + /// New created field + static Field create_field(const FieldStart & field_start, + CenterStarts &tiny_starts, + const Lines & lines, + const SampleConfig &config); + }; } // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp index 7506052602..ff5968625f 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp @@ -19,6 +19,8 @@ struct SupportIslandPoint center_line, center_line_end, // end of branch center_line_end2, // start of main path(only one per VD) + center_line_end3, // end in continous sampling + center_line_start, // first sample center_circle, center_circle_end, // circle finish by one point (one end per circle - // need allign) @@ -51,7 +53,9 @@ struct SupportIslandPoint Type::one_center_point, Type::two_points, Type::center_line_end, - Type::center_line_end2}); + Type::center_line_end2, + Type::center_line_end3, + Type::center_line_start}); return cant_move.find(type) == cant_move.end(); //*/ } diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index 6c07445cc5..a4d68c934d 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -41,6 +41,18 @@ Slic3r::Vec2d VoronoiGraphUtils::to_point_d(const VD::vertex_type *vertex) return Vec2d(vertex->x(), vertex->y()); } +Slic3r::Point VoronoiGraphUtils::to_direction(const VD::edge_type *edge) +{ + return to_direction_d(edge).cast(); +} + +Slic3r::Vec2d VoronoiGraphUtils::to_direction_d(const VD::edge_type *edge) +{ + const VD::vertex_type *v0 = edge->vertex0(); + const VD::vertex_type *v1 = edge->vertex1(); + return Vec2d(v1->x() - v0->x(), v1->y() - v0->y()); +} + bool VoronoiGraphUtils::is_coord_in_limits(const VD::coordinate_type &coord, const coord_t & source, double max_distance) @@ -768,16 +780,28 @@ VoronoiGraph::ExPath VoronoiGraphUtils::create_longest_path( return longest_path; } -const VoronoiGraph::Node *VoronoiGraphUtils::get_twin_node(const VoronoiGraph::Node::Neighbor *neighbor) +const VoronoiGraph::Node::Neighbor *VoronoiGraphUtils::get_twin(const VoronoiGraph::Node::Neighbor *neighbor) { auto twin_edge = neighbor->edge->twin(); - for (const VoronoiGraph::Node::Neighbor n : neighbor->node->neighbors) { - if (n.edge == twin_edge) return n.node; + for (const VoronoiGraph::Node::Neighbor &twin_neighbor : neighbor->node->neighbors) { + if (twin_neighbor.edge == twin_edge) return &twin_neighbor; } assert(false); return nullptr; } +const VoronoiGraph::Node *VoronoiGraphUtils::get_twin_node(const VoronoiGraph::Node::Neighbor *neighbor) +{ + return get_twin(neighbor)->node; +} + +bool VoronoiGraphUtils::is_opposit_direction(const VD::edge_type *edge, const Line &line) +{ + Point dir_line = LineUtils::direction(line); + Point dir_edge = VoronoiGraphUtils::to_direction(edge); + return !PointUtils::is_same_direction(dir_line, dir_edge); +} + Slic3r::Point VoronoiGraphUtils::create_edge_point( const VoronoiGraph::Position &position) { @@ -810,6 +834,84 @@ Slic3r::Point VoronoiGraphUtils::create_edge_point(const VD::edge_type *edge, return Point(v0->x() + dir.x(), v0->y() + dir.y()); } +VoronoiGraph::Position VoronoiGraphUtils::get_position_with_distance( + const VoronoiGraph::Node::Neighbor *neighbor, coord_t width, const Slic3r::Lines &lines) +{ + VoronoiGraph::Position result(neighbor, 0.); + const VD::edge_type *edge = neighbor->edge; + if (edge->is_curved()) { + // Every point on curve has same distance from outline + return result; + } + assert(edge->is_finite()); + Slic3r::Line edge_line(to_point(edge->vertex0()), to_point(edge->vertex1())); + const Slic3r::Line &source_line = lines[edge->cell()->source_index()]; + if (LineUtils::is_parallel(edge_line, source_line)) { + // Every point on parallel lines has same distance + return result; + } + + double half_width = width / 2.; + + double a_dist = source_line.perp_distance_to(edge_line.a); + double b_dist = source_line.perp_distance_to(edge_line.b); + + // check if half_width is in range from a_dist to b_dist + if (a_dist > b_dist) { + if (b_dist >= half_width) { + // vertex1 is closer to width + result.ratio = 1.; + return result; + } else if (a_dist <= half_width) { + // vertex0 is closer to width + return result; + } + } else { + // a_dist < b_dist + if (a_dist >= half_width) { + // vertex0 is closer to width + return result; + } else if (b_dist <= half_width) { + // vertex1 is closer to width + result.ratio = 1.; + return result; + } + } + result.ratio = fabs((a_dist - half_width) / (a_dist - b_dist)); + return result; +} + +Slic3r::Point VoronoiGraphUtils::point_on_line( + const VoronoiGraph::Position &position, const Line &line) +{ + const VD::edge_type* edge = position.neighbor->edge; + assert(edge->is_linear()); + Point edge_point = create_edge_point(position); + Point dir = to_point(edge->vertex0()) - to_point(edge->vertex1()); + Line edge_line(edge_point, edge_point + PointUtils::perp(dir)); + std::optional intersection = LineUtils::intersection(line, edge_line); + assert(intersection.has_value()); + return intersection->cast(); +} + +std::pair VoronoiGraphUtils::point_on_lines( + const VoronoiGraph::Position &position, + const Line & first, + const Line & second) +{ + const VD::edge_type *edge = position.neighbor->edge; + assert(edge->is_linear()); + Point edge_point = create_edge_point(position); + Point dir = to_point(edge->vertex0()) - to_point(edge->vertex1()); + Line edge_line(edge_point, edge_point + PointUtils::perp(dir)); + std::optional first_intersection = LineUtils::intersection(first, edge_line); + assert(first_intersection.has_value()); + std::optional second_intersection = LineUtils::intersection(second, edge_line); + assert(second_intersection.has_value()); + return {first_intersection->cast(), + second_intersection->cast()}; +} + VoronoiGraph::Position VoronoiGraphUtils::align( const VoronoiGraph::Position &position, const Point &to, double max_distance) { diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp index c56020564e..3e2b01f1d1 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp @@ -38,14 +38,28 @@ public: /// /// Input point pointer(double precission) /// Convertedf point(int preccission) - static Slic3r::Point to_point(const VD::vertex_type *vertex); + static Point to_point(const VD::vertex_type *vertex); /// /// Convert point type between voronoi and slicer format /// /// Input vertex /// created vector - static Slic3r::Vec2d to_point_d(const VD::vertex_type* vertex); + static Vec2d to_point_d(const VD::vertex_type* vertex); + + /// + /// create direction from Voronoi edge + /// + /// input + /// direction --> (vertex1 - vertex0) + static Point to_direction(const VD::edge_type *edge); + + /// + /// create direction from Voronoi edge + /// + /// input + /// direction --> (vertex1 - vertex0) + static Vec2d to_direction_d(const VD::edge_type *edge); /// /// check if coord is in limits for coord_t @@ -70,13 +84,13 @@ public: double max_distance); /// - /// Private function to help convert edge without vertex to line + /// PRIVATE: function to help convert edge without vertex to line /// /// VD source point /// VD source point /// Maximal distance from source point /// Line segment between lines - static Slic3r::Line create_line_between_source_points( + static Line create_line_between_source_points( const Point &point1, const Point &point2, double maximal_distance); /// @@ -89,9 +103,9 @@ public: /// Source point for voronoi diagram /// Maximal distance from source point /// Croped line, when all line segment is out of max distance return empty optional - static std::optional to_line(const VD::edge_type &edge, - const Points & points, - double maximal_distance); + static std::optional to_line(const VD::edge_type &edge, + const Points & points, + double maximal_distance); /// /// close polygon defined by lines /// close points will convert to their center @@ -103,11 +117,11 @@ public: /// Merge points closer than minimal_distance /// Count checking points, create help points for result polygon /// Valid CCW polygon with center inside of polygon - static Slic3r::Polygon to_polygon(const Lines &lines, - const Point ¢er, - double maximal_distance, - double minimal_distance, - size_t count_points); + static Polygon to_polygon(const Lines &lines, + const Point ¢er, + double maximal_distance, + double minimal_distance, + size_t count_points); /// /// Convert cell to polygon /// Source for VD must be only point to create VD with only line segments @@ -117,9 +131,9 @@ public: /// source points for VD /// maximal distance from source point - only for infinite edges(cells) /// polygon created by cell - static Slic3r::Polygon to_polygon(const VD::cell_type & cell, - const Slic3r::Points &points, - double maximal_distance); + static Polygon to_polygon(const VD::cell_type &cell, + const Points & points, + double maximal_distance); // return node from graph by vertex, when no exists create one static VoronoiGraph::Node *getNode(VoronoiGraph & graph, @@ -148,18 +162,37 @@ public: /// Point from source points. static const Point& retrieve_point(const Points &points, const VD::cell_type &cell); - static Slic3r::Point get_parabola_point(const VD::edge_type ¶bola, const Slic3r::Lines &lines); - static Slic3r::Line get_parabola_line(const VD::edge_type ¶bola, const Lines &lines); + /// + /// PRIVATE: function to get parabola focus point + /// + /// curved edge + /// source lines + /// Parabola focus point + static Point get_parabola_point(const VD::edge_type ¶bola, const Lines &lines); + + /// + /// PRIVATE: + /// + /// + /// + /// + static Line get_parabola_line(const VD::edge_type ¶bola, const Lines &lines); + + /// + /// Construct parabola from curved edge + /// + /// curved edge + /// Voronoi diagram source lines + /// Parabola represented shape of edge static Parabola get_parabola(const VD::edge_type &edge, const Lines &lines); /// - /// Calculate length + /// Calculate length of curved edge /// /// curved edge + /// Voronoi diagram source lines /// edge length - static double calculate_length_of_parabola( - const VD::edge_type & edge, - const Lines & lines); + static double calculate_length_of_parabola(const VD::edge_type &edge, const Lines &lines); /// /// Calculate length of edge line segment or curve - parabola. @@ -167,8 +200,7 @@ public: /// Input edge to calcuate length /// Source for Voronoi diagram. It contains parabola parameters /// The length of edge - static double calculate_length(const VD::edge_type &edge, - const Lines & lines); + static double calculate_length(const VD::edge_type &edge, const Lines &lines); /// /// Calculate maximal distance to outline and multiply by two(must be similar on both side) @@ -176,8 +208,7 @@ public: /// Input edge. /// Source for Voronoi diagram. It contains parabola parameters /// Maximal island width along edge - static double calculate_max_width(const VD::edge_type &edge, - const Lines & lines); + static double calculate_max_width(const VD::edge_type &edge, const Lines &lines); /// /// calculate distances to border of island and length on skeleton @@ -285,13 +316,27 @@ public: static VoronoiGraph::ExPath create_longest_path( const VoronoiGraph::Node *start_node); + /// + /// Find twin neighbor + /// + /// neighbor + /// Twin neighbor + static const VoronoiGraph::Node::Neighbor *get_twin(const VoronoiGraph::Node::Neighbor *neighbor); + /// /// Find source node of neighbor /// /// neighbor /// start node - static const VoronoiGraph::Node *VoronoiGraphUtils::get_twin_node( - const VoronoiGraph::Node::Neighbor *neighbor); + static const VoronoiGraph::Node *get_twin_node(const VoronoiGraph::Node::Neighbor *neighbor); + + /// + /// Check if neighbor is in opposit direction to line direction + /// + /// edge has direction from vertex0 to vertex1 + /// line has direction from point a to point b + /// True when oposit direction otherwise FALSE + static bool is_opposit_direction(const VD::edge_type *edge, const Line &line); /// /// Create point on edge defined by neighbor @@ -302,6 +347,39 @@ public: static Point create_edge_point(const VoronoiGraph::Position& position); static Point create_edge_point(const VD::edge_type *edge, double ratio); + /// + /// Find position on VD edge with width + /// + /// Edge for searching position + /// Specify place on edge + /// Source lines for voronoi diagram + /// Position on given edge + static VoronoiGraph::Position get_position_with_distance( + const VoronoiGraph::Node::Neighbor *neighbor, + coord_t width, + const Lines & lines); + + /// + /// Calculate point on line correspond to edge position + /// + /// Position on edge + /// Line must be source of edge + /// Point lay on line defined by position on edge + static Point point_on_line(const VoronoiGraph::Position& position, const Line& line); + + /// + /// calculate both point on source lines correspond to edge postion + /// Faster way to get both point_on_line + /// + /// Position on edge + /// Line must be source of edge + /// Line must be source of edge + /// pair of point lay on lines cirrespond to position + static std::pair point_on_lines( + const VoronoiGraph::Position &position, + const Line & first, + const Line & second); + /// /// align "position" close to point "to" /// diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 3a6d15aee7..c71be6dc54 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -373,13 +373,15 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island, SampleConfig create_sample_config(double size) { SampleConfig cfg; - cfg.max_distance = 3 * size + 0.1; + cfg.max_distance = 3 * size + 0.1; + cfg.half_distance = cfg.max_distance/2; cfg.head_radius = size / 4; cfg.minimal_distance_from_outline = cfg.head_radius + size/10; + cfg.minimal_support_distance = cfg.minimal_distance_from_outline + cfg.half_distance; cfg.max_length_for_one_support_point = 2*size; cfg.max_length_for_two_support_points = 4*size; - cfg.max_width_for_center_supportr_line = size; - cfg.max_width_for_zig_zag_supportr_line = 2*size; + cfg.max_width_for_center_support_line = size; + cfg.min_width_for_outline_support = cfg.max_width_for_center_support_line; cfg.minimal_move = std::max(1000., size/1000); cfg.count_iteration = 100; @@ -408,7 +410,7 @@ TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]") for (int i = 0; i < 100; ++i) { VoronoiGraph::ExPath longest_path; VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines); - auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path); + auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, lines, cfg, longest_path); } } @@ -430,7 +432,7 @@ TEST_CASE("Speed align", "[VoronoiSkeleton]") VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines); for (int i = 0; i < 100; ++i) { - auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path); + auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, lines, cfg, longest_path); SampleIslandUtils::align_samples(samples, island, cfg); } }