diff --git a/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp b/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp index 4410ecb347..df46546ec1 100644 --- a/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp +++ b/src/libslic3r/SLA/SupportIslands/ExpandNeighbor.cpp @@ -28,7 +28,7 @@ void ExpandNeighbor::process(CallStack &call_stack) const VoronoiGraph::Node &next_node = *neighbor.node; // is next node leaf ? if (next_node.neighbors.size() == 1) { - VoronoiGraph::Path side_branch({&next_node}, neighbor.edge_length); + VoronoiGraph::Path side_branch({&next_node}, neighbor.length()); data.side_branches.push(std::move(side_branch)); return; } @@ -39,6 +39,6 @@ void ExpandNeighbor::process(CallStack &call_stack) call_stack.emplace(std::move(post_process_neighbor)); call_stack.emplace( std::make_unique(neighbor_path, neighbor.node, - neighbor.edge_length, + neighbor.length(), data.act_path)); } \ No newline at end of file diff --git a/src/libslic3r/SLA/SupportIslands/ParabolaUtils.cpp b/src/libslic3r/SLA/SupportIslands/ParabolaUtils.cpp index f8d4c24629..6dbca2cad2 100644 --- a/src/libslic3r/SLA/SupportIslands/ParabolaUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/ParabolaUtils.cpp @@ -72,6 +72,30 @@ bool ParabolaUtils::is_over_zero(const ParabolaSegment ¶bola) return is_positive_x1 != is_positive_x2; } +void ParabolaUtils::draw(SVG & svg, + const ParabolaSegment ¶bola, + const char * color, + coord_t width, + double discretization_step) +{ + using VD = Slic3r::Geometry::VoronoiDiagram; + std::vector parabola_samples( + {parabola.from, parabola.to}); + + VD::point_type source_point = parabola.focus; + VD::segment_type source_segment(parabola.directrix.a, + parabola.directrix.b); + ::boost::polygon::voronoi_visual_utils::discretize( + source_point, source_segment, discretization_step, ¶bola_samples); + + for (size_t index = 1; index < parabola_samples.size(); ++index) { + const auto& s0 = parabola_samples[index - 1]; + const auto& s1 = parabola_samples[index]; + Line l(Point(s0.x(), s0.y()), Point(s1.x(), s1.y())); + svg.draw(l, color, width); + } +} + // PRIVATE double ParabolaUtils::parabola_arc_length(double x) { diff --git a/src/libslic3r/SLA/SupportIslands/ParabolaUtils.hpp b/src/libslic3r/SLA/SupportIslands/ParabolaUtils.hpp index a0912fff8a..460914fd2f 100644 --- a/src/libslic3r/SLA/SupportIslands/ParabolaUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/ParabolaUtils.hpp @@ -2,6 +2,7 @@ #define slic3r_SLA_SuppotstIslands_ParabolaUtils_hpp_ #include "Parabola.hpp" +#include namespace Slic3r::sla { @@ -44,6 +45,20 @@ public: /// True when interval contain top of parabola otherwise False static bool is_over_zero(const ParabolaSegment ¶bola); + /// + /// Connvert parabola to svg by sampling + /// + /// outputfile + /// parabola to draw + /// color + /// width + /// step between discretized lines + static void draw(SVG & svg, + const ParabolaSegment ¶bola, + const char * color, + coord_t width, + double discretization_step = 1e3); + private: /// /// Integral of parabola: y = x^2 from zero to point x diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index b931823bfe..332816dbf3 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -17,7 +17,7 @@ #include // allign // comment definition of NDEBUG to enable assert() -// #define NDEBUG +//#define NDEBUG #define SLA_SAMPLING_STORE_FIELD_TO_SVG #define SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG @@ -50,11 +50,11 @@ std::unique_ptr SampleIslandUtils::create_point_on_path( } const VoronoiGraph::Node::Neighbor *neighbor = VoronoiGraphUtils::get_neighbor(prev_node, node); - actual_distance += neighbor->edge_length; + actual_distance += neighbor->length(); if (actual_distance >= distance) { // over half point is on double previous_distance = actual_distance - distance; - double over_ratio = previous_distance / neighbor->edge_length; + double over_ratio = previous_distance / neighbor->length(); double ratio = 1. - over_ratio; return create_point(neighbor, ratio, type); } @@ -126,14 +126,14 @@ SupportIslandPoints SampleIslandUtils::sample_side_branch( std::move_iterator(side_samples.end())); } } - while (distance < neighbor->edge_length) { - double edge_ratio = distance / neighbor->edge_length; + while (distance < neighbor->length()) { + double edge_ratio = distance / neighbor->length(); result.push_back( create_point(neighbor, edge_ratio, SupportIslandPoint::Type::center_line) ); distance += sample_distance; } - distance -= neighbor->edge_length; + distance -= neighbor->length(); prev_node = node; } assert(fabs(distance - (sample_distance - cfg.side_distance)) < 1e-5); @@ -310,12 +310,12 @@ void SampleIslandUtils::sample_center_circle_end( const CenterLineConfiguration & cfg, SupportIslandPoints & result) { - double distance = neighbor_distance + node_distance + neighbor.edge_length; + double distance = neighbor_distance + node_distance + neighbor.length(); if (distance < cfg.max_sample_distance) { // no need add support point - if (neighbor_distance > node_distance + neighbor.edge_length) - neighbor_distance = node_distance + neighbor.edge_length; - if (node_distance > neighbor_distance + neighbor.edge_length) - node_distance = neighbor_distance + neighbor.edge_length; + if (neighbor_distance > node_distance + neighbor.length()) + neighbor_distance = node_distance + neighbor.length(); + if (node_distance > neighbor_distance + neighbor.length()) + node_distance = neighbor_distance + neighbor.length(); return; } size_t count_supports = static_cast( @@ -328,11 +328,11 @@ void SampleIslandUtils::sample_center_circle_end( neighbor_distance = 0.; count_supports -= 1; if (count_supports == 0) { - if (node_distance > neighbor.edge_length) - node_distance = neighbor.edge_length; + if (node_distance > neighbor.length()) + node_distance = neighbor.length(); return; } - distance = node_distance + neighbor.edge_length; + distance = node_distance + neighbor.length(); distance_between = distance / (count_supports + 1); } VoronoiGraph::Nodes nodes = done_nodes; // copy, could be more neighbor @@ -341,7 +341,7 @@ void SampleIslandUtils::sample_center_circle_end( double distance_from_neighbor = i * (distance_between) - neighbor_distance; result.push_back( create_point_on_path(nodes, distance_from_neighbor, SupportIslandPoint::Type::center_circle_end2)); - double distance_support_to_node = fabs(neighbor.edge_length - + double distance_support_to_node = fabs(neighbor.length() - distance_from_neighbor); if (node_distance > distance_support_to_node) node_distance = distance_support_to_node; @@ -396,7 +396,7 @@ double get_distance_to_support_point(const VoronoiGraph::Node *node, }; std::priority_queue, OrderDistanceFromNearest> process; for (const VoronoiGraph::Node::Neighbor &neighbor : node->neighbors) - process.emplace(node, neighbor.node, neighbor.edge_length); + process.emplace(node, neighbor.node, neighbor.length()); while (!process.empty()) { Item i = process.top(); @@ -411,7 +411,7 @@ double get_distance_to_support_point(const VoronoiGraph::Node *node, } for (const VoronoiGraph::Node::Neighbor &neighbor :i.node->neighbors) { if (neighbor.node == i.prev_node) continue; - double distance = i.act_distance + neighbor.edge_length; + double distance = i.act_distance + neighbor.length(); if (distance > maximal_search) continue; process.emplace(i.node, neighbor.node, distance); } @@ -443,8 +443,8 @@ SupportDistanceMap create_support_distance_map(const SupportIslandPoints &suppor const VoronoiGraph::Position &position = ptr->position; const VoronoiGraph::Node *node = position.neighbor->node; const VoronoiGraph::Node *twin_node = VoronoiGraphUtils::get_twin_node(position.neighbor); - double distance = (1 - position.ratio) * position.neighbor->edge_length; - double twin_distance = position.ratio * position.neighbor->edge_length; + double distance = (1 - position.ratio) * position.neighbor->length(); + double twin_distance = position.ratio * position.neighbor->length(); auto item = support_distance_map.find(node); if (item == support_distance_map.end()) { @@ -584,14 +584,14 @@ SupportIslandPoints SampleIslandUtils::sample_center_circle( NodeDistance next_nd = nd; // copy next_nd.nodes.insert(next_nd.nodes.begin(), neighbor.node); - next_nd.distance_from_support_point += neighbor.edge_length; + next_nd.distance_from_support_point += neighbor.length(); // exist place for sample: while (next_nd.distance_from_support_point > cfg.max_sample_distance) { double distance_from_node = next_nd .distance_from_support_point - nd.distance_from_support_point; - double ratio = distance_from_node / neighbor.edge_length; + double ratio = distance_from_node / neighbor.length(); result.push_back( create_point(&neighbor, ratio, SupportIslandPoint::Type::center_circle)); next_nd.distance_from_support_point -= cfg.max_sample_distance; @@ -602,20 +602,28 @@ SupportIslandPoints SampleIslandUtils::sample_center_circle( return result; } -void SampleIslandUtils::sample_field( - VoronoiGraph::Position& field_start, - SupportIslandPoints& points, - CenterStarts& center_starts, - std::set& done, - const Lines & lines, - const SampleConfig &config) +SupportIslandPoints SampleIslandUtils::sample_voronoi_graph( + const VoronoiGraph & graph, + const Lines & lines, + const SampleConfig & config, + VoronoiGraph::ExPath &longest_path) { - auto field = create_field(field_start, center_starts, done, lines, config); - SupportIslandPoints outline_support = sample_outline(field, config); - points.insert(points.end(), std::move_iterator(outline_support.begin()), - std::move_iterator(outline_support.end())); - // TODO: sample field inside + const VoronoiGraph::Node *start_node = + VoronoiGraphUtils::getFirstContourNode(graph); + // every island has to have a point on contour + 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)); + VoronoiGraphUtils::draw(svg, graph, lines, 1e6, true); + } +#endif // SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG + return sample_expath(longest_path, lines, config); } SupportIslandPoints SampleIslandUtils::sample_expath( @@ -653,8 +661,7 @@ SupportIslandPoints SampleIslandUtils::sample_expath( // TODO: 3) Triangle of points // eval outline and find three point create almost equilateral triangle - // IMPROVE: Erase continous sampling: Extract path and than sample uniformly whole path - + // IMPROVE: Erase continous sampling: Extract ExPath and than sample uniformly whole ExPath CenterStarts center_starts; const VoronoiGraph::Node *start_node = path.nodes.front(); // CHECK> Front of path is outline node @@ -662,26 +669,19 @@ SupportIslandPoints SampleIslandUtils::sample_expath( const VoronoiGraph::Node::Neighbor *neighbor = &start_node->neighbors.front(); std::set done; // already done nodes SupportIslandPoints points; // result - if (neighbor->max_width > config.max_width_for_center_support_line) { - VoronoiGraph::Position field_start = VoronoiGraphUtils::get_position_with_distance( - neighbor, config.min_width_for_outline_support, lines); - double center_sample_distance = neighbor->edge_length * field_start.ratio; - if (center_sample_distance > config.max_distance / 2.) { - // sample field from node, start without change on begining - sample_field(field_start, points, center_starts, done, lines, config); - } else { - const VoronoiGraph::Node::Neighbor *twin = VoronoiGraphUtils::get_twin(neighbor); - done.insert(neighbor->node); - coord_t support_in = neighbor->edge_length - center_sample_distance + - config.max_distance / 2; - center_starts.push(CenterStart(twin, support_in, {neighbor->node})); - sample_field(field_start, points, center_starts, done, lines, config); - } - } else { + if (neighbor->max_width() < config.max_width_for_center_support_line) { + // start sample center done.insert(start_node); - center_starts.push(CenterStart(neighbor, config.minimal_distance_from_outline)); + coord_t support_in = config.minimal_distance_from_outline; + center_starts.push(CenterStart(neighbor, support_in)); + } else { + // start sample field + VoronoiGraph::Position field_start = + VoronoiGraphUtils::get_position_with_distance( + neighbor, config.min_width_for_outline_support, lines); + sample_field(field_start, points, center_starts, done, lines, config); } - + // Main loop of sampling while (!center_starts.empty()) { std::optional field_start = {}; std::vector new_starts = @@ -693,10 +693,23 @@ SupportIslandPoints SampleIslandUtils::sample_expath( field_start = {}; } } - return points; } +void SampleIslandUtils::sample_field(VoronoiGraph::Position &field_start, + SupportIslandPoints & points, + CenterStarts & center_starts, + std::set &done, + const Lines & lines, + const SampleConfig &config) +{ + auto field = create_field(field_start, center_starts, done, lines, config); + SupportIslandPoints outline_support = sample_outline(field, config); + points.insert(points.end(), std::move_iterator(outline_support.begin()), + std::move_iterator(outline_support.end())); + // TODO: sample field inside +} + std::vector SampleIslandUtils::sample_center( const CenterStart & start, const SampleConfig & config, @@ -707,13 +720,13 @@ std::vector SampleIslandUtils::sample_center( { const VoronoiGraph::Node::Neighbor *neighbor = start.neighbor; const VoronoiGraph::Node *node = neighbor->node; + // already sampled line 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; do { - double edge_length = neighbor->edge_length; + double edge_length = neighbor->length(); while (edge_length >= support_in) { double ratio = support_in / edge_length; results.push_back( @@ -724,6 +737,7 @@ std::vector SampleIslandUtils::sample_center( support_in -= edge_length; const VoronoiGraph::Node *node = neighbor->node; path.push_back(node); + done.insert(node); const VoronoiGraph::Node::Neighbor *next_neighbor = nullptr; for (const auto &node_neighbor : node->neighbors) { if (done.find(node_neighbor.node) != done.end()) continue; @@ -754,11 +768,14 @@ std::vector SampleIslandUtils::sample_center( } else { neighbor = next_neighbor; } - } while (neighbor->max_width <= config.max_width_for_center_support_line); + } while (neighbor->max_width() <= config.max_width_for_center_support_line); + // create field start field_start = VoronoiGraphUtils::get_position_with_distance( neighbor, config.min_width_for_outline_support, lines); - double edge_length = neighbor->edge_length; + + // sample rest of neighbor before field + double edge_length = neighbor->length(); double sample_length = edge_length * field_start->ratio; while (sample_length > support_in) { double ratio = support_in / edge_length; @@ -769,29 +786,6 @@ std::vector SampleIslandUtils::sample_center( return new_starts; } -SupportIslandPoints SampleIslandUtils::sample_voronoi_graph( - const VoronoiGraph & graph, - const Lines & lines, - const SampleConfig & config, - VoronoiGraph::ExPath &longest_path) -{ - const VoronoiGraph::Node *start_node = - VoronoiGraphUtils::getFirstContourNode(graph); - // every island has to have a point on contour - 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); -} SampleIslandUtils::Field SampleIslandUtils::create_field( const VoronoiGraph::Position & field_start, @@ -844,10 +838,11 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( const VoronoiGraph::Node * source_node)->bool { const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor; - // TODO: check not only one neighbor but all path to edge + // IMPROVE: check not only one neighbor but all path to edge + coord_t rest_size = static_cast(neighbor->length() * (1. - position.ratio)); if (VoronoiGraphUtils::is_last_neighbor(neighbor) && - neighbor->edge_length * (1. - position.ratio) <= config.max_distance / 2) - return false; + rest_size <= config.max_distance / 2) + return false; // no change only rich outline // function to add sorted change from wide to tiny // stored uder line index or line shorten in point b @@ -864,8 +859,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( }; Point p1, p2; - std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, - lines); + std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines); const VD::edge_type *edge = neighbor->edge; size_t i1 = edge->cell()->source_index(); size_t i2 = edge->twin()->cell()->source_index(); @@ -879,7 +873,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( // line1 is shorten on side line1.b add(p1, p2, i1, i2); } - coord_t support_in = neighbor->edge_length * position.ratio + config.max_distance/2; + coord_t support_in = neighbor->length() * position.ratio + config.max_distance/2; CenterStart tiny_start(neighbor, support_in, {source_node}); tiny_starts.push(tiny_start); tiny_done.insert(source_node); @@ -893,14 +887,16 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( std::set done; done.insert(twin_neighbor->node); - std::queue process; - process.push(neighbor->node); + using ProcessItem = std::pair; + std::queue process; + process.push({twin_neighbor->node, neighbor->node}); // all lines belongs to polygon std::set field_line_indexes; while (!process.empty()) { - const VoronoiGraph::Node *node = process.front(); - const VoronoiGraph::Node *prev_node = nullptr; + const ProcessItem &item = process.front(); + const VoronoiGraph::Node *node = item.second; + const VoronoiGraph::Node *prev_node = item.first; process.pop(); if (done.find(node) != done.end()) continue; do { @@ -913,7 +909,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( size_t index2 = edge->twin()->cell()->source_index(); field_line_indexes.insert(index1); field_line_indexes.insert(index2); - if (VoronoiGraphUtils::is_last_neighbor(&neighbor) || neighbor.max_width < min_width) { + if (neighbor.min_width() < min_width) { VoronoiGraph::Position position = VoronoiGraphUtils::get_position_with_distance(&neighbor, min_width, lines); if(add_wide_tiny_change(position, node)) @@ -923,7 +919,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( if (next_node == nullptr) { next_node = neighbor.node; } else { - process.push(neighbor.node); + process.push({node, neighbor.node}); } } prev_node = node; @@ -996,7 +992,9 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( 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 input_index1 = neighbor->edge->cell()->source_index(); + size_t input_index2 = neighbor->edge->twin()->cell()->source_index(); + size_t input_index = std::min(input_index1, input_index2); size_t outline_index = input_index; std::set done_indexes; do { diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp index 884c5d6575..72da95cf22 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -108,17 +108,6 @@ public: const CenterLineConfiguration & cfg, SupportIslandPoints & result); - /// - /// 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 Lines & lines, - const SampleConfig &config); - /// /// Sample voronoi skeleton /// @@ -134,6 +123,18 @@ public: const SampleConfig & config, VoronoiGraph::ExPath &longest_path); + /// + /// 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 Lines & lines, + const SampleConfig &config); + + /// /// Transform support point to slicer points /// @@ -250,9 +251,9 @@ public: /// /// Create field from input neighbor /// - /// Start neighbor, first occur of wide neighbor. + /// Start position, change from tiny to wide. /// Append new founded tiny parts of island. - /// Already sampled node sets. Filled only node inside field imediate after change + /// Already sampled node sets. /// Source lines for VG --> outline of island. /// Containe Minimal width in field and sample distance for center line /// New created field diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp index 1bc66f915c..bbfe6298c2 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp @@ -57,23 +57,40 @@ struct VoronoiGraph::Node::Neighbor const VD::edge_type *edge; // pointer on graph node structure const Node *node; + + /// + /// DTO represents size property of one Neighbor + /// + struct Size{ + // length edge between vertices + double length; - // length edge between vertices - double edge_length; + // widht is distance between outlines + // maximal width + coord_t min_width; + // minimal widht + coord_t max_width; - // maximal widht of sland(distance to outline) - double max_width; + Size(double length, coord_t min_width, coord_t max_width) + : length(length), min_width(min_width), max_width(max_width) + {} + }; + std::shared_ptr size; public: - Neighbor(const VD::edge_type *edge, const Node *node, double edge_length, double max_width) + Neighbor(const VD::edge_type * edge, + const Node * node, + std::shared_ptr size) : edge(edge) , node(node) - , edge_length(edge_length) - , max_width(max_width) + , size(std::move(size)) {} + // accessor to member + double length() const { return size->length; } + coord_t min_width() const { return size->min_width; } + coord_t max_width() const { return size->max_width; } }; - /// /// DTO represents path over nodes of VoronoiGraph /// store queue of Nodes diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index 544aa2629b..367ec17ab9 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -423,6 +423,79 @@ double VoronoiGraphUtils::calculate_max_width( return max_width(parabola.focus); } +std::pair VoronoiGraphUtils::calculate_width( + const VD::edge_type &edge, const Lines &lines) +{ + if (edge.is_linear()) + return calculate_width_for_line(edge, lines); + return calculate_width_for_parabola(edge, lines); +} + +std::pair VoronoiGraphUtils::calculate_width_for_line( + const VD::edge_type &line_edge, const Lines &lines) +{ + assert(line_edge.is_linear()); + // edge line could be initialized by 2 points + if (line_edge.cell()->contains_point()) { + const Line &source_line = lines[line_edge.cell()->source_index()]; + Point source_point; + if (line_edge.cell()->source_category() == + boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) + source_point = source_line.a; + else { + assert(line_edge.cell()->source_category() == + boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT); + source_point = source_line.b; + } + return min_max_width(line_edge, source_point); + } + assert(line_edge.cell()->contains_segment()); + assert(!line_edge.twin()->cell()->contains_point()); + assert(line_edge.twin()->cell()->contains_segment()); + const Line & line = lines[line_edge.cell()->source_index()]; + Point v0 = to_point(line_edge.vertex0()); + Point v1 = to_point(line_edge.vertex1()); + double distance0 = line.perp_distance_to(v0); + double distance1 = line.perp_distance_to(v1); + std::pair min_max(2 * static_cast(distance0), + 2 * static_cast(distance1)); + if (min_max.first > min_max.second) + std::swap(min_max.first, min_max.second); + return min_max; +} + +std::pair VoronoiGraphUtils::calculate_width_for_parabola( + const VD::edge_type ¶bola_edge, const Lines &lines) +{ + assert(parabola_edge.is_curved()); + // distance to point and line on parabola is same + Parabola parabola = get_parabola(parabola_edge, lines); + Point v0 = to_point(parabola_edge.vertex0()); + Point v1 = to_point(parabola_edge.vertex1()); + ParabolaSegment parabola_segment(parabola, v0, v1); + std::pair min_max = min_max_width(parabola_edge, parabola.focus); + if (ParabolaUtils::is_over_zero(parabola_segment)) { + min_max.first = parabola.directrix.perp_distance_to(parabola.focus); + } + return min_max; +} + +std::pair VoronoiGraphUtils::min_max_width( + const VD::edge_type &edge, const Point &point) +{ + auto distance = [](const VD::vertex_type *vertex, + const Point & point) -> coord_t { + Vec2d point_d = point.cast(); + Vec2d diff = point_d - to_point_d(vertex); + double distance = diff.norm(); + return static_cast(std::round(distance)); + }; + std::pair result(2 * distance(edge.vertex0(), point), + 2 * distance(edge.vertex1(), point)); + if (result.first > result.second) std::swap(result.first, result.second); + return result; +}; + VoronoiGraph VoronoiGraphUtils::create_skeleton(const VD &vd, const Lines &lines) { // vd should be annotated. @@ -458,16 +531,16 @@ VoronoiGraph VoronoiGraphUtils::create_skeleton(const VD &vd, const Lines &lines return {}; // vd must be annotated double length = calculate_length(edge, lines); - double max_width = calculate_max_width(edge, lines); + coord_t min_width, max_width; + std::tie(min_width, max_width) = calculate_width(edge, lines); + auto neighbor_size = std::make_shared( + length, min_width, max_width); + VoronoiGraph::Node *node0 = getNode(skeleton, v0, &edge, lines); VoronoiGraph::Node *node1 = getNode(skeleton, v1, &edge, lines); - - // TODO: Do not store twice length and max_width. // add extended Edge to graph, both side - VoronoiGraph::Node::Neighbor neighbor0(&edge, node1, length, max_width); - node0->neighbors.push_back(neighbor0); - VoronoiGraph::Node::Neighbor neighbor1(edge.twin(), node0, length, max_width); - node1->neighbors.push_back(neighbor1); + node0->neighbors.emplace_back(&edge, node1, neighbor_size); + node1->neighbors.emplace_back(edge.twin(), node0, std::move(neighbor_size)); } return skeleton; } @@ -485,7 +558,7 @@ double VoronoiGraphUtils::get_neighbor_distance(const VoronoiGraph::Node *from, { const VoronoiGraph::Node::Neighbor *neighbor = get_neighbor(from, to); assert(neighbor != nullptr); - return neighbor->edge_length; + return neighbor->length(); } VoronoiGraph::Path VoronoiGraphUtils::find_longest_path_on_circle( @@ -608,7 +681,7 @@ VoronoiGraph::Path VoronoiGraphUtils::find_longest_path_on_circles( continue; // out of circles if (done.find(neighbor.node) != done.end()) continue; VoronoiGraph::Path neighbor_path = path; // make copy - neighbor_path.append(neighbor.node, neighbor.edge_length); + neighbor_path.append(neighbor.node, neighbor.length()); search_queue.push(neighbor_path); auto branches_item = ex_path.side_branches.find(neighbor.node); @@ -655,7 +728,7 @@ std::optional VoronoiGraphUtils::create_circle( VoronoiGraph::Nodes circle_path(path_item, passed_nodes.end()); // !!! Real circle lenght is calculated on detection of end circle // now circle_length contain also lenght of path before circle - double circle_length = path.length + neighbor.edge_length; + double circle_length = path.length + neighbor.length(); // solve of branch length will be at begin of cirlce return VoronoiGraph::Circle(std::move(circle_path), circle_length); }; @@ -933,12 +1006,12 @@ VoronoiGraph::Position VoronoiGraphUtils::align( }; std::queue process; const VoronoiGraph::Node::Neighbor* neighbor = position.neighbor; - double from_distance = neighbor->edge_length * position.ratio; + double from_distance = neighbor->length() * position.ratio; if (from_distance < max_distance) { const VoronoiGraph::Node *from_node = VoronoiGraphUtils::get_twin_node(neighbor); process.emplace(from_node, from_distance); } - double to_distance = neighbor->edge_length * (1 - position.ratio); + double to_distance = neighbor->length() * (1 - position.ratio); if (to_distance < max_distance) { const VoronoiGraph::Node *to_node = neighbor->node; process.emplace(to_node, to_distance); @@ -966,7 +1039,7 @@ VoronoiGraph::Position VoronoiGraphUtils::align( closest_distance = distance; closest = VoronoiGraph::Position(&neighbor, ratio); } - double from_start = nd.distance + neighbor.edge_length; + double from_start = nd.distance + neighbor.length(); if (from_start < max_distance) process.emplace(neighbor.node, from_start); } @@ -1013,9 +1086,9 @@ const VoronoiGraph::Node *VoronoiGraphUtils::getFirstContourNode( return nullptr; } -double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path) +coord_t VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path) { - double max = 0.; + coord_t max = 0; const VoronoiGraph::Node *prev_node = nullptr; for (const VoronoiGraph::Node *node : path) { if (prev_node == nullptr) { @@ -1023,16 +1096,16 @@ double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path) continue; } const VoronoiGraph::Node::Neighbor *neighbor = get_neighbor(prev_node, node); - if (max < neighbor->max_width) max = neighbor->max_width; + if (max < neighbor->max_width()) max = neighbor->max_width(); prev_node = node; } return max; } -double VoronoiGraphUtils::get_max_width( +coord_t VoronoiGraphUtils::get_max_width( const VoronoiGraph::ExPath &longest_path) { - double max = get_max_width(longest_path.nodes); + coord_t max = get_max_width(longest_path.nodes); for (const auto &side_branches_item : longest_path.side_branches) { const VoronoiGraph::Node *prev_node = side_branches_item.first; VoronoiGraph::ExPath::SideBranches side_branches = side_branches_item.second; // !!! copy @@ -1040,8 +1113,8 @@ double VoronoiGraphUtils::get_max_width( const VoronoiGraph::Path &side_path = side_branches.top(); const VoronoiGraph::Node::Neighbor *first_neighbor = get_neighbor(prev_node, side_path.nodes.front()); - double max_side_branch = std::max( - get_max_width(side_path.nodes), first_neighbor->max_width); + coord_t max_side_branch = std::max( + get_max_width(side_path.nodes), first_neighbor->max_width()); if (max < max_side_branch) max = max_side_branch; side_branches.pop(); } @@ -1051,7 +1124,7 @@ double VoronoiGraphUtils::get_max_width( const VoronoiGraph::Node::Neighbor *first_neighbor = get_neighbor(circle.nodes.front(), circle.nodes.back()); double max_circle = std::max( - first_neighbor->max_width, get_max_width(circle.nodes)); + first_neighbor->max_width(), get_max_width(circle.nodes)); if (max < max_circle) max = max_circle; } @@ -1059,9 +1132,9 @@ double VoronoiGraphUtils::get_max_width( } // !!! is slower than go along path -double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node) +coord_t VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node) { - double max = 0.; + coord_t max = 0; std::set done; std::queue process; process.push(node); @@ -1072,7 +1145,7 @@ double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node) for (const VoronoiGraph::Node::Neighbor& neighbor: actual_node->neighbors) { if (done.find(neighbor.node) != done.end()) continue; process.push(neighbor.node); - if (max < neighbor.max_width) max = neighbor.max_width; + if (max < neighbor.max_width()) max = neighbor.max_width(); } done.insert(actual_node); } @@ -1081,13 +1154,16 @@ double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node) void VoronoiGraphUtils::draw(SVG & svg, const VoronoiGraph &graph, + const Lines & lines, coord_t width, - bool pointer_caption) + bool pointer_caption) { + LineUtils::draw(svg, lines, "black", 0., true); + auto print_address = [&](const Point& p, const char* prefix, void * addr, const char* color){ if (pointer_caption) { std::stringstream ss; - ss << prefix << std::hex << (int) addr; + ss << prefix << std::hex << reinterpret_cast(addr); std::string s = ss.str(); svg.draw_text(p, s.c_str(), color); } @@ -1096,7 +1172,7 @@ void VoronoiGraphUtils::draw(SVG & svg, for (const auto &[key, value] : graph.data) { Point p(key->x(), key->y()); svg.draw(p, "lightgray", width); - print_address(p, "v_",(void*)key, "lightgray"); + print_address(p, "vertex ptr ",(void*)key, "lightgray"); for (const auto &n : value.neighbors) { Point from = to_point(n.edge->vertex0()); Point to = to_point(n.edge->vertex1()); @@ -1104,17 +1180,34 @@ void VoronoiGraphUtils::draw(SVG & svg, Point center = (from + to) / 2; Point p = center + ((is_second) ? Point(0., -2e6) : Point(0., 2e6)); - print_address(p, "n_", (void *) &n, "gray"); + print_address(p, "neighbor ptr ", (void *) &n, "gray"); if (is_second) continue; - svg.draw_text(center + Point(-6e6, 0.), ("w="+std::to_string(n.max_width)).c_str(), "gray"); - svg.draw(Line(from, to), "gray", width); - - // svg.draw_text(center, - // (std::to_string(std::round(n.edge_length/3e5)/100.)).c_str(), "gray"); + std::string width_str = "width min=" + std::to_string(n.min_width()) + + " max=" + std::to_string(n.max_width()); + svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), "gray"); + draw(svg, *n.edge, lines, "gray", width); } } } +void VoronoiGraphUtils::draw(SVG & svg, + const VD::edge_type &edge, + const Lines & lines, + const char * color, + coord_t width) +{ + Point from = to_point(edge.vertex0()); + Point to = to_point(edge.vertex1()); + if (edge.is_curved()) { + Parabola p = get_parabola(edge, lines); + ParabolaSegment ps(p, from, to); + ParabolaUtils::draw(svg, ps, color, width); + return; + } + svg.draw(Line(from, to), color, width); +} + + void VoronoiGraphUtils::draw(SVG & svg, const VoronoiGraph::Nodes &path, coord_t width, diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp index b804ed8b26..143158d06a 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp @@ -162,6 +162,7 @@ public: /// Point from source points. static const Point& retrieve_point(const Points &points, const VD::cell_type &cell); +private: /// /// PRIVATE: function to get parabola focus point /// @@ -171,13 +172,14 @@ public: static Point get_parabola_point(const VD::edge_type ¶bola, const Lines &lines); /// - /// PRIVATE: + /// PRIVATE: function to get parabola diretrix /// - /// - /// - /// + /// curved edge + /// source lines + /// Parabola diretrix static Line get_parabola_line(const VD::edge_type ¶bola, const Lines &lines); +public: /// /// Construct parabola from curved edge /// @@ -204,12 +206,31 @@ public: /// /// Calculate maximal distance to outline and multiply by two(must be similar on both side) + /// ! not used /// /// 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); + /// + /// Calculate width limit(min, max) and round value to coord_t + /// + /// Input edge + /// Source for Voronoi diagram. It contains parabola parameters + /// Width range for edge. + /// First is minimal width on edge. + /// Second is maximal width on edge. + static std::pair calculate_width(const VD::edge_type &edge, const Lines &lines); + +private: + static std::pair calculate_width_for_line( + const VD::edge_type &line_edge, const Lines &lines); + static std::pair calculate_width_for_parabola( + const VD::edge_type ¶bola_edge, const Lines &lines); + static std::pair min_max_width(const VD::edge_type &edge, const Point &point); + +public: /// /// calculate distances to border of island and length on skeleton /// @@ -405,9 +426,9 @@ public: /// /// Input point to voronoi graph /// Maximal widht in graph - static double get_max_width(const VoronoiGraph::ExPath &longest_path); - static double get_max_width(const VoronoiGraph::Nodes &path); - static double get_max_width(const VoronoiGraph::Node *node); + static coord_t get_max_width(const VoronoiGraph::ExPath &longest_path); + static coord_t get_max_width(const VoronoiGraph::Nodes &path); + static coord_t get_max_width(const VoronoiGraph::Node *node); /// /// Check if neighbor is end of VG @@ -417,7 +438,16 @@ public: static bool is_last_neighbor(const VoronoiGraph::Node::Neighbor *neighbor); public: // draw function for debug - static void draw(SVG &svg, const VoronoiGraph &graph, coord_t width, bool pointer_caption = false); + static void draw(SVG & svg, + const VoronoiGraph &graph, + const Lines & lines, + coord_t width, + bool pointer_caption = false); + static void draw(SVG & svg, + const VD::edge_type &edge, + const Lines & lines, + const char * color, + coord_t width); static void draw(SVG & svg, const VoronoiGraph::Nodes &path, coord_t width, diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 4612661bd9..a440834a63 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -218,7 +218,23 @@ Slic3r::Polygon create_V_shape(double height, double line_width, double angle = return polygons.front(); } -ExPolygon create_tiny_wide_test(double wide, double tiny) +ExPolygon create_tiny_wide_test_1(double wide, double tiny) +{ + double hole_size = wide; + double width = 2 * wide + hole_size; + double height = wide + hole_size + tiny; + auto outline = PolygonUtils::create_rect(width, height); + auto hole = PolygonUtils::create_rect(hole_size, hole_size); + hole.reverse(); + int hole_move_y = height/2 - (hole_size/2 + tiny); + hole.translate(0, hole_move_y); + + ExPolygon result(outline); + result.holes = {hole}; + return result; +} + +ExPolygon create_tiny_wide_test_2(double wide, double tiny) { double hole_size = wide; double width = (3 + 1) * wide + 3 * hole_size; @@ -290,7 +306,9 @@ ExPolygons createTestIslands(double size) 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), + ExPolygon(PolygonUtils::create_isosceles_triangle(5. * size, 40. * size)), + create_tiny_wide_test_1(3 * size, 2 / 3. * size), + create_tiny_wide_test_2(3 * size, 2 / 3. * size), // still problem // three support points