diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp index bb4f7a753f..3c0cc73e33 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.cpp @@ -279,6 +279,30 @@ std::optional LineUtils::intersection(const Line &ray1, const Lin return (ray1.a.cast() + t * v1); } +bool LineUtils::belongs(const Line &line, const Point &point, double benevolence) +{ + const Point &a = line.a; + const Point &b = line.b; + auto is_in_interval = [](coord_t value, coord_t from, coord_t to) -> bool + { + if (from > to) { + if (from > value || to < value) return false; + } else { + if (from < value || to > value) return false; + } + return true; + }; + + if (!is_in_interval(point.x(), a.x(), b.x()) || + !is_in_interval(point.y(), a.y(), b.y()) ) + { // out of interval + return false; + } + double distance = line.perp_distance_to(point); + if (distance < benevolence) return true; + return false; +} + double LineUtils::foot(const Line &line, const Point &point) { Vec2d a = line.a.cast(); diff --git a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp index cd29843528..23e1cbe79e 100644 --- a/src/libslic3r/SLA/SupportIslands/LineUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/LineUtils.hpp @@ -111,6 +111,17 @@ public: /// intersection point when exist static std::optional intersection(const Line &ray1, const Line &ray2); + /// + /// Check when point lays on line and belongs line range(from point a to point b) + /// + /// source line + /// point to check + /// maximal distance from line to belongs line + /// True when points lay between line.a and line.b + static bool belongs(const Line & line, + const Point &point, + double benevolence = 1.); + /// /// Create direction from point a to point b /// Direction vector is represented as point diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp index d3627b91d5..6d4546bc89 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp @@ -24,6 +24,11 @@ struct SampleConfig // zero when head should be on outline coord_t minimal_distance_from_outline = 0; // [nano meter] + // Measured as sum of VD edge length from outline + // Used only when there is no space for outline offset on first/last point + // Must be bigger than minimal_distance_from_outline + coord_t maximal_distance_from_outline = 1.;// [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; @@ -47,6 +52,7 @@ struct SampleConfig // 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 diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp index 7f82ab4ad7..63ea56047c 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp @@ -27,7 +27,9 @@ public: result.max_distance = max_distance / sample_multiplicator; result.half_distance = result.max_distance / 2; result.head_radius = head_diameter / 2; - result.minimal_distance_from_outline = head_diameter / 2.; + result.minimal_distance_from_outline = result.head_radius; + result.maximal_distance_from_outline = result.half_distance; + assert(result.minimal_distance_from_outline < result.maximal_distance_from_outline); result.minimal_support_distance = result.minimal_distance_from_outline + result.half_distance; @@ -76,7 +78,7 @@ public: // Align support points // TODO: propagate print resolution - result.minimal_move = 1000; // [in nanometers], devide from print resolution to quater pixel + result.minimal_move = 10000.;// [in nanometers --> 0.01mm ], devide from print resolution to quater pixel result.count_iteration = 100; // speed VS precission result.max_align_distance = result.max_distance / 2; diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp index 4f6682275b..e1764e3c4d 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -20,9 +20,10 @@ // comment definition of NDEBUG to enable assert() //#define NDEBUG -//#define SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG -//#define SLA_SAMPLING_STORE_FIELD_TO_SVG -//#define SLA_SAMPLING_STORE_POISSON_SAMPLING_TO_SVG +#define SLA_SAMPLE_ISLAND_UTILS_STORE_VORONOI_GRAPH_TO_SVG +#define SLA_SAMPLE_ISLAND_UTILS_STORE_INITIAL_SAMPLE_POSITION_TO_SVG +#define SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG +//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG #include @@ -131,14 +132,21 @@ Slic3r::Points SampleIslandUtils::sample_expolygon(const ExPolygon &expoly, return result; } -std::unique_ptr SampleIslandUtils::create_point( +SupportIslandPointPtr SampleIslandUtils::create_no_move_point( const VoronoiGraph::Node::Neighbor *neighbor, double ratio, SupportIslandPoint::Type type) { VoronoiGraph::Position position(neighbor, ratio); - Slic3r::Point p = VoronoiGraphUtils::create_edge_point(position); - return std::make_unique(p, type); + return create_no_move_point(position, type); +} + +SupportIslandPointPtr SampleIslandUtils::create_no_move_point( + const VoronoiGraph::Position &position, + SupportIslandPoint::Type type) +{ + Point point = VoronoiGraphUtils::create_edge_point(position); + return std::make_unique(point, type); } std::optional SampleIslandUtils::create_position_on_path( @@ -157,9 +165,8 @@ std::unique_ptr SampleIslandUtils::create_point( 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->length(); - double ratio = 1. - over_ratio; + double behind_position = actual_distance - distance; + double ratio = 1. - behind_position / neighbor->length(); return VoronoiGraph::Position(neighbor, ratio); } prev_node = node; @@ -171,7 +178,50 @@ std::unique_ptr SampleIslandUtils::create_point( return {}; // unreachable } -SupportIslandPointPtr SampleIslandUtils::create_point_on_path( +std::optional +SampleIslandUtils::create_position_on_path(const VoronoiGraph::Nodes &path, + const Lines & lines, + coord_t width, + coord_t &max_distance) +{ + const VoronoiGraph::Node *prev_node = nullptr; + coord_t actual_distance = 0; + for (const VoronoiGraph::Node *node : path) { + if (prev_node == nullptr) { // first call + prev_node = node; + continue; + } + const VoronoiGraph::Node::Neighbor *neighbor = + VoronoiGraphUtils::get_neighbor(prev_node, node); + + if (width <= neighbor->max_width()) { + VoronoiGraph::Position position = VoronoiGraphUtils::get_position_with_width(neighbor, width, lines); + // set max distance to actual distance + coord_t rest_distance = position.calc_distance(); + coord_t distance = actual_distance + rest_distance; + if (max_distance > distance) { + max_distance = distance; + return position; + } + } + + actual_distance += static_cast(neighbor->length()); + if (actual_distance >= max_distance) { + // over half point is on + coord_t behind_position = actual_distance - max_distance; + double ratio = 1. - behind_position / neighbor->length(); + return VoronoiGraph::Position(neighbor, ratio); + } + prev_node = node; + } + + // distance must be inside path + // this means bad input params + assert(false); + return {}; // unreachable +} + +SupportIslandPointPtr SampleIslandUtils::create_center_island_point( const VoronoiGraph::Nodes &path, double distance, const SampleConfig & config, @@ -187,24 +237,27 @@ SupportIslandPointPtr SampleIslandUtils::create_middle_path_point( { auto position_opt = create_position_on_path(path.nodes, path.length / 2); if (!position_opt.has_value()) return nullptr; - - return create_point(position_opt->neighbor, position_opt->ratio, type); + return create_no_move_point(*position_opt, type); } SupportIslandPoints SampleIslandUtils::create_side_points( - const VoronoiGraph::Nodes &path, double side_distance) + const VoronoiGraph::Nodes &path, + const Lines& lines, + coord_t width, + coord_t max_side_distance) { VoronoiGraph::Nodes reverse_path = path; // copy std::reverse(reverse_path.begin(), reverse_path.end()); - auto pos1 = create_position_on_path(path, side_distance); - auto pos2 = create_position_on_path(reverse_path, side_distance); + coord_t distance2 = max_side_distance; // copy + auto pos1 = create_position_on_path(path, lines, width, max_side_distance); + auto pos2 = create_position_on_path(reverse_path, lines, width, distance2); assert(pos1.has_value()); assert(pos2.has_value()); SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points; SupportIslandPoints result; result.reserve(2); - result.push_back(create_point(pos1->neighbor, pos1->ratio, type)); - result.push_back(create_point(pos2->neighbor, pos2->ratio, type)); + result.push_back(create_no_move_point(*pos1, type)); + result.push_back(create_no_move_point(*pos2, type)); return result; } @@ -225,7 +278,7 @@ SupportIslandPoints SampleIslandUtils::sample_side_branch( reverse_path.push_back(first_node); SupportIslandPoints result; result.push_back( - create_point_on_path(reverse_path, side_distance, cfg.sample_config, + create_center_island_point(reverse_path, side_distance, cfg.sample_config, SupportIslandPoint::Type::center_line_end)); return result; } @@ -255,7 +308,8 @@ SupportIslandPoints SampleIslandUtils::sample_side_branch( while (distance < neighbor->length()) { double edge_ratio = distance / neighbor->length(); result.push_back( - create_point(neighbor, edge_ratio, SupportIslandPoint::Type::center_line) + create_no_move_point(neighbor, edge_ratio, + SupportIslandPoint::Type::center_line) ); distance += sample_distance; } @@ -371,8 +425,6 @@ bool is_points_in_distance(const Slic3r::Point & p, return true; } -//#define VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE - coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples, const ExPolygon & island, const SampleConfig & config) @@ -381,22 +433,37 @@ coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples, using VD = Slic3r::Geometry::VoronoiDiagram; VD vd; Slic3r::Points points = SampleIslandUtils::to_points(samples); + coord_t max_move = 0; -#ifdef VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG + std::string color_of_island = "#FF8080"; // LightRed. Should not be visible - cell color should overlap + std::string color_point_cell = "lightgray"; // bigger than island but NOT self overlap + std::string color_island_cell_intersection = "gray"; // Should full overlap island !! + std::string color_old_point = "lightblue"; // Center of island cell intersection + std::string color_wanted_point = "darkblue"; // Center of island cell intersection + std::string color_new_point = "blue"; // Center of island cell intersection + std::string color_static_point = "black"; static int counter = 0; BoundingBox bbox(island); - SVG svg(("align_"+std::to_string(counter++)+".svg").c_str(), bbox); - svg.draw(island, "lightblue"); -#endif // VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE + + SVG svg(("align_" + std::to_string(counter++) + ".svg").c_str(), bbox); + svg.draw(island, color_of_island ); +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG // create voronoi diagram with points construct_voronoi(points.begin(), points.end(), &vd); - coord_t max_move = 0; for (const VD::cell_type &cell : vd.cells()) { SupportIslandPointPtr &sample = samples[cell.source_index()]; + +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG + if (!sample->can_move()) { + svg.draw(sample->point, color_static_point, config.head_radius); + svg.draw_text(sample->point+Point(config.head_radius,0), SupportIslandPoint::to_string(sample->type).c_str(), color_static_point.c_str()); + } +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG if (!sample->can_move()) continue; - Polygon polygon = VoronoiGraphUtils::to_polygon(cell, points, config.max_distance); - Polygons intersections = Slic3r::intersection(island, ExPolygon(polygon)); + Polygon cell_polygon = VoronoiGraphUtils::to_polygon(cell, points, config.max_distance); + Polygons intersections = Slic3r::intersection(island, ExPolygon(cell_polygon)); const Polygon *island_cell = nullptr; for (const Polygon &intersection : intersections) { if (intersection.contains(sample->point)) { @@ -406,16 +473,32 @@ coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples, } assert(island_cell != nullptr); Point center = island_cell->centroid(); + { + SVG cell_svg("island_cell.svg", island_cell->points); + cell_svg.draw(cell_polygon, "lightgray"); + cell_svg.draw(points, "darkgray", config.head_radius); + cell_svg.draw(*island_cell, "gray"); + cell_svg.draw(sample->point, "green", config.head_radius); + cell_svg.draw(center, "black", config.head_radius); + } assert(is_points_in_distance(center, island_cell->points, config.max_distance)); -#ifdef VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE - svg.draw(polygon, "lightgray"); - svg.draw(*island_cell, "gray"); - svg.draw(sample.point, "black", config.head_radius); - svg.draw(Line(sample.point, center), "blue", config.head_radius / 5); -#endif // VISUALIZE_SAMPLE_ISLAND_UTILS_ALIGN_ONCE +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG + svg.draw(cell_polygon, color_point_cell); + svg.draw(*island_cell, color_island_cell_intersection); + svg.draw(Line(sample->point, center), color_wanted_point, config.head_radius / 5); + svg.draw(sample->point, color_old_point, config.head_radius); + svg.draw(center, color_wanted_point, config.head_radius); // wanted position +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG coord_t act_move = sample->move(center); if (max_move < act_move) max_move = act_move; +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG + svg.draw(sample->point, color_new_point, config.head_radius); + svg.draw_text(sample->point+Point(config.head_radius,0), SupportIslandPoint::to_string(sample->type).c_str(), color_new_point.c_str()); +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG } +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG + svg.Close(); +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG return max_move; } @@ -459,8 +542,8 @@ void SampleIslandUtils::sample_center_circle_end( double distance_between = distance / (count_supports + 1); if (distance_between < neighbor_distance) { // point is calculated to be in done path, SP will be on edge point - result.push_back( - create_point(&neighbor, 1., SupportIslandPoint::Type::center_circle_end)); + result.push_back(create_no_move_point( + &neighbor, 1., SupportIslandPoint::Type::center_circle_end)); neighbor_distance = 0.; count_supports -= 1; if (count_supports == 0) { @@ -478,8 +561,7 @@ void SampleIslandUtils::sample_center_circle_end( auto position = create_position_on_path(nodes, distance_from_neighbor); assert(position.has_value()); result.push_back( - create_point(position->neighbor, position->ratio, - SupportIslandPoint::Type::center_circle_end2)); + create_no_move_point(*position, SupportIslandPoint::Type::center_circle_end2)); double distance_support_to_node = fabs(neighbor.length() - distance_from_neighbor); if (node_distance > distance_support_to_node) @@ -581,7 +663,7 @@ SupportDistanceMap create_support_distance_map(const SupportIslandPoints &suppor auto ptr = dynamic_cast(support_point.get()); // bad use const VoronoiGraph::Position &position = ptr->position; const VoronoiGraph::Node *node = position.neighbor->node; - const VoronoiGraph::Node *twin_node = VoronoiGraphUtils::get_twin_node(position.neighbor); + const VoronoiGraph::Node *twin_node = VoronoiGraphUtils::get_twin_node(*position.neighbor); double distance = (1 - position.ratio) * position.neighbor->length(); double twin_distance = position.ratio * position.neighbor->length(); @@ -759,15 +841,27 @@ SupportIslandPoints SampleIslandUtils::sample_voronoi_graph( longest_path = VoronoiGraphUtils::create_longest_path(start_node); // longest_path = create_longest_path_recursive(start_node); -#ifdef SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG +#ifdef SLA_SAMPLE_ISLAND_UTILS_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); +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_VORONOI_GRAPH_TO_SVG + + SupportIslandPoints points = sample_expath(longest_path, lines, config); + +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_INITIAL_SAMPLE_POSITION_TO_SVG + { + static int counter = 0; + SVG svg("initial_sample_positions" + std::to_string(counter++) + ".svg", + LineUtils::create_bounding_box(lines)); + svg.draw(lines, "gray", config.head_radius/ 10); + draw(svg, points, config.head_radius, "black", true); + } +#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_INITIAL_SAMPLE_POSITION_TO_SVG + return points; } SupportIslandPoints SampleIslandUtils::sample_expath( @@ -787,16 +881,20 @@ SupportIslandPoints SampleIslandUtils::sample_expath( double max_width = VoronoiGraphUtils::get_max_width(path); 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, - config.minimal_distance_from_outline); + if (path.length < config.max_length_for_two_support_points) { + coord_t max_distance = + std::min(config.half_distance, static_cast(path.length / 2)); + return create_side_points(path.nodes, lines, + 2 * config.minimal_distance_from_outline, + max_distance); + } // othewise sample path - CenterLineConfiguration + /*CenterLineConfiguration centerLineConfiguration(path.side_branches, config); SupportIslandPoints samples = sample_center_line(path, centerLineConfiguration); samples.front()->type = SupportIslandPoint::Type::center_line_end2; - return samples; + return samples;*/ } // TODO: 3) Triangle of points @@ -812,26 +910,42 @@ SupportIslandPoints SampleIslandUtils::sample_expath( SupportIslandPoints points; // result if (neighbor->max_width() < config.max_width_for_center_support_line) { // start sample center - done.insert(start_node); - coord_t support_in = config.minimal_distance_from_outline; - center_starts.push(CenterStart(neighbor, support_in)); + coord_t width = 2 * config.minimal_distance_from_outline; + coord_t distance = config.maximal_distance_from_outline; + auto position = create_position_on_path(path.nodes, lines, width, distance); + if (position.has_value()) { + points.push_back(create_no_move_point( + *position, SupportIslandPoint::Type::center_line_start)); + VoronoiGraph::Nodes start_path; + for (const auto &node : path.nodes) { + if (node == position->neighbor->node) break; + done.insert(node); + start_path.push_back(node); + } + coord_t already_supported = position->calc_distance(); + coord_t support_in = config.max_distance + already_supported; + center_starts.emplace_back(position->neighbor, support_in, start_path); + } else { + assert(position.has_value()); + done.insert(start_node); + coord_t support_in = config.minimal_distance_from_outline; + center_starts.emplace_back(neighbor, support_in); + } + // IMPROVE: check side branches on start path } else { // start sample field VoronoiGraph::Position field_start = - VoronoiGraphUtils::get_position_with_distance( + VoronoiGraphUtils::get_position_with_width( 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 = - sample_center(center_starts.front(), config, done, points, lines, field_start); - center_starts.pop(); - for (const CenterStart &start : new_starts) center_starts.push(start); - if (field_start.has_value()){ // exist new field start? - sample_field(*field_start, points, center_starts, done, lines, config); - } + std::optional field_start = + sample_center(center_starts, done, points, lines, config); + while (field_start.has_value()) { + sample_field(*field_start, points, center_starts, done, lines, config); + field_start = sample_center(center_starts, done, points, lines, config); } return points; } @@ -868,34 +982,54 @@ void SampleIslandUtils::sample_field(VoronoiGraph::Position &field_start, }); } -std::vector SampleIslandUtils::sample_center( - const CenterStart & start, - const SampleConfig & config, +std::optional SampleIslandUtils::sample_center( + CenterStarts & new_starts, std::set &done, SupportIslandPoints & results, const Lines & lines, - std::optional &field_start) + const SampleConfig & config) { - const VoronoiGraph::Node::Neighbor *neighbor = start.neighbor; - const VoronoiGraph::Node *node = neighbor->node; - // already sampled line - if (done.find(node) != done.end()) return {}; - VoronoiGraph::Nodes path = start.path; - std::vector new_starts; - double support_in = start.support_in; - do { - double edge_length = neighbor->length(); + const VoronoiGraph::Node::Neighbor *neighbor = nullptr; + VoronoiGraph::Nodes path; + coord_t support_in; + bool use_new_start = true; + bool is_continous = false; + while (use_new_start || neighbor->max_width() <= config.max_width_for_center_support_line) { + // !! do not check max width for new start, it could be wide to tiny change + if (use_new_start) { + use_new_start = false; + // skip done starts + if (new_starts.empty()) return {}; // no start + while (done.find(new_starts.back().neighbor->node) != done.end()) { + new_starts.pop_back(); + if (new_starts.empty()) return {}; + } + // fill new start + const CenterStart & new_start = new_starts.back(); + neighbor = new_start.neighbor; + path = new_start.path; // copy + support_in = new_start.support_in; + new_starts.pop_back(); + is_continous = false; + } + + // add support on actual neighbor edge + coord_t edge_length = static_cast(neighbor->length()); while (edge_length >= support_in) { - double ratio = support_in / edge_length; + double ratio = support_in / neighbor->length(); VoronoiGraph::Position position(neighbor, ratio); results.push_back(std::make_unique( position, &config, SupportIslandPoint::Type::center_line)); support_in += config.max_distance; + is_continous = true; } support_in -= edge_length; + const VoronoiGraph::Node *node = neighbor->node; - path.push_back(node); done.insert(node); + // IMPROVE: A) limit length of path to config.minimal_support_distance + // IMPROVE: B) store node in reverse order + 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; @@ -903,42 +1037,38 @@ std::vector SampleIslandUtils::sample_center( next_neighbor = &node_neighbor; continue; } - double next_support_in = (support_in < config.half_distance) ? - support_in : config.max_distance - support_in; + coord_t 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()); - auto position_opt = create_position_on_path( - path_reverse, config.minimal_distance_from_outline); - assert(position_opt.has_value()); - Point point = VoronoiGraphUtils::create_edge_point( - *position_opt); - results.push_back(std::make_unique( - point, SupportIslandPoint::Type::center_line_end3)); + VoronoiGraph::Nodes path_reverse = path; // copy + std::reverse(path_reverse.begin(), path_reverse.end()); + coord_t width = 2 * config.minimal_distance_from_outline; + coord_t distance = config.maximal_distance_from_outline; + auto position_opt = create_position_on_path(path_reverse, lines, width, distance); + if (position_opt.has_value()) { + if (is_continous && config.max_distance < (support_in+distance) ) { + // one support point should be enough + // when max_distance > maximal_distance_from_outline + results.pop_back(); // remove support point + } + create_sample_center_end(*position_opt, results, new_starts, config); } - - 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(); + use_new_start = true; } else { neighbor = next_neighbor; } - } while (neighbor->max_width() <= config.max_width_for_center_support_line); + } // create field start - field_start = VoronoiGraphUtils::get_position_with_distance( + auto result = VoronoiGraphUtils::get_position_with_width( neighbor, config.min_width_for_outline_support, lines); // sample rest of neighbor before field double edge_length = neighbor->length(); - double sample_length = edge_length * field_start->ratio; + double sample_length = edge_length * result.ratio; while (sample_length > support_in) { double ratio = support_in / edge_length; VoronoiGraph::Position position(neighbor, ratio); @@ -946,7 +1076,81 @@ std::vector SampleIslandUtils::sample_center( position, &config,SupportIslandPoint::Type::center_line)); support_in += config.max_distance; } - return new_starts; + return result; +} + +void SampleIslandUtils::create_sample_center_end( + const VoronoiGraph::Position &position, + SupportIslandPoints & results, + CenterStarts & new_starts, + const SampleConfig & config) +{ + const SupportIslandPoint::Type no_move_type = + SupportIslandPoint::Type::center_line_end3; + const coord_t minimal_support_distance = config.minimal_support_distance; + Point point = VoronoiGraphUtils::create_edge_point(position); + // raw pointers are function scope ONLY + std::vector near_no_move; + for (const auto &res : results) { + if (res->type != no_move_type) continue; + Point diff = point - res->point; + if (abs(diff.x()) > minimal_support_distance) continue; + if (abs(diff.y()) > minimal_support_distance) continue; + near_no_move.push_back( + &*res); // create raw pointer, used only in function scope + } + + std::map distances; + std::function + collect_distances = [&](const auto &neighbor, coord_t act_distance) { + distances[&neighbor] = act_distance; + }; + VoronoiGraphUtils::for_neighbor_at_distance(position, minimal_support_distance, collect_distances); + + bool exist_no_move = false; + if (!near_no_move.empty()) { + for (const auto &item : distances) { + const VoronoiGraph::Node::Neighbor &neighbor = *item.first; + // TODO: create belongs for parabola, when start sampling at parabola + Line edge(VoronoiGraphUtils::to_point(neighbor.edge->vertex0()), + VoronoiGraphUtils::to_point(neighbor.edge->vertex1())); + for (const auto &support_point : near_no_move) { + if (LineUtils::belongs(edge, support_point->point)) { + exist_no_move = true; + break; + } + } + if (exist_no_move) break; + } + } + + if (!exist_no_move) { + // fix value of support_in + // for new_starts in sampled path + // by distance to position + for (CenterStart &new_start : new_starts) { + auto item = distances.find(new_start.neighbor); + if (item != distances.end()) { + coord_t support_distance = item->second; + coord_t new_support_in = config.max_distance - item->second; + new_start.support_in = std::max(new_start.support_in, + new_support_in); + } else { + const VoronoiGraph::Node::Neighbor *twin = + VoronoiGraphUtils::get_twin(*new_start.neighbor); + auto item = distances.find(twin); + if (item != distances.end()) { + coord_t support_distance = item->second + twin->length(); + coord_t new_support_in = config.max_distance - + support_distance; + new_start.support_in = std::max(new_start.support_in, + new_support_in); + } + } + } + results.push_back( + std::make_unique(point, no_move_type)); + } } @@ -1039,13 +1243,13 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( } 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_starts.push_back(tiny_start); tiny_done.insert(source_node); return true; }; const VoronoiGraph::Node::Neighbor *tiny_wide_neighbor = field_start.neighbor; - const VoronoiGraph::Node::Neighbor *wide_tiny_neighbor = VoronoiGraphUtils::get_twin(tiny_wide_neighbor); + const VoronoiGraph::Node::Neighbor *wide_tiny_neighbor = VoronoiGraphUtils::get_twin(*tiny_wide_neighbor); VoronoiGraph::Position position(wide_tiny_neighbor, 1. - field_start.ratio); add_wide_tiny_change(position, tiny_wide_neighbor->node); @@ -1078,7 +1282,8 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( field_line_indexes.insert(index2); if (neighbor.min_width() < min_width) { VoronoiGraph::Position position = - VoronoiGraphUtils::get_position_with_distance(&neighbor, min_width, lines); + VoronoiGraphUtils::get_position_with_width( + &neighbor, min_width, lines); if(add_wide_tiny_change(position, node)) continue; } @@ -1194,7 +1399,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( field.source_indexe_for_change = source_indexe_for_change; field.source_indexes = std::move(source_indexes); -#ifdef SLA_SAMPLING_STORE_FIELD_TO_SVG +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG { const char *source_line_color = "black"; bool draw_source_line_indexes = true; @@ -1207,7 +1412,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field( LineUtils::draw(svg, lines, source_line_color, 0., draw_source_line_indexes); draw(svg, field, draw_border_line_indexes, draw_field_source_indexes); } -#endif //SLA_SAMPLING_STORE_FIELD_TO_SVG +#endif //SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG return field; } @@ -1406,6 +1611,7 @@ SupportIslandPoints SampleIslandUtils::sample_outline( // initialize first index if (inner_first == inner_invalid) inner_first = inner_last; } + add_lines_samples(inner_lines, inner_first, inner_last); } }; @@ -1458,3 +1664,23 @@ void SampleIslandUtils::draw(SVG & svg, } } } + +bool SampleIslandUtils::is_visualization_disabled() +{ +#ifndef NDEBUG + return false; +#endif +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_VORONOI_GRAPH_TO_SVG + return false; +#endif +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_INITIAL_SAMPLE_POSITION_TO_SVG + return false; +#endif +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG + return false; +#endif +#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG + return false; +#endif + return true; +} diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp index a20ffcde42..1b35aa77db 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -47,11 +47,21 @@ public: /// Source distance ratio for position on edge /// Type of point /// created support island point - static SupportIslandPointPtr create_point( + static SupportIslandPointPtr create_no_move_point( const VoronoiGraph::Node::Neighbor *neighbor, double ratio, SupportIslandPoint::Type type = SupportIslandPoint::Type::undefined); + /// + /// Create unique static support point + /// + /// Define position on VD + /// Type of support point + /// new created support point + static SupportIslandPointPtr create_no_move_point( + const VoronoiGraph::Position &position, + SupportIslandPoint::Type type); + /// /// Find point lay on path with distance from first point on path /// @@ -63,7 +73,33 @@ public: const VoronoiGraph::Nodes &path, double distance); - static SupportIslandPointPtr create_point_on_path( + /// + /// Find first point lay on sequence of node + /// where widht are equal second params OR + /// distance from first node is exactly max distance + /// Depends which occure first + /// + /// Sequence of nodes, should be longer than max distance + /// Source lines for VG --> params for parabola. + /// Width of island(2x distance to outline) + /// Maximal distance from first node on path. + /// At end is set to actual distance from first node. + /// Position when exists + static std::optional create_position_on_path( + const VoronoiGraph::Nodes &path, + const Lines & lines, + coord_t width, + coord_t & max_distance); + + /// + /// Create SupportCenterIslandPoint laying on Voronoi Graph + /// + /// VD path + /// Distance on path + /// Configuration + /// Type of point + /// Support island point + static SupportIslandPointPtr create_center_island_point( const VoronoiGraph::Nodes &path, double distance, const SampleConfig & config, @@ -81,8 +117,19 @@ public: const VoronoiGraph::Path &path, SupportIslandPoint::Type type = SupportIslandPoint::Type::undefined); - // create 2 points on both ends of path with side distance from border - static SupportIslandPoints create_side_points(const VoronoiGraph::Nodes &path, double side_distance); + /// + /// create points on both ends of path with side distance from border + /// + /// Longest path over island. + /// Source lines for VG --> outline of island. + /// Wanted width on position + /// Maximal distance from side + /// 2x Static Support point(lay os sides of path) + static SupportIslandPoints create_side_points( + const VoronoiGraph::Nodes & path, + const Lines & lines, + coord_t width, + coord_t max_side_distance); // DTO with data for sampling line in center struct CenterLineConfiguration @@ -180,6 +227,7 @@ public: /// Sampling configuration /// Maximal distance between neighbor points + /// Term criteria for align: Minimal sample move and Maximal count of iteration + /// Maximal distance of move during aligning. static coord_t align_once(SupportIslandPoints &samples, const ExPolygon & island, const SampleConfig & config); @@ -192,7 +240,7 @@ public: /// Maximal search distance on VD for closest point /// Distance move of original point static coord_t align_support(SupportIslandPoint &support, const Point &wanted, double max_distance); - + /// /// DTO hold information for start sampling VD in center /// @@ -212,28 +260,44 @@ public: : neighbor(neighbor), support_in(support_in), path(path) {} }; - using CenterStarts = std::queue; + using CenterStarts = std::vector; /// /// Sample VG in center --> sample tiny part of island - /// Detection of wide neighbor which start field - /// Creating of new starts (from VG cross -> multi neighbors) + /// Sample until starts are empty or find new field(wide neighbor). + /// Creating of new starts (by VG cross -> multi neighbors) /// /// Start to sample - /// Parameters for sampling - /// Already done nodes + /// Already done(processed) nodes /// Result of sampling - /// Source line for VD. To decide position of change from tiny to wide part - /// Output: Wide neighbor, start of field - /// New start of sampling - static std::vector sample_center( - const CenterStart & start, - const SampleConfig & config, + /// Source line for VD. To decide position of change from tiny to wide part + /// Parameters for sampling + /// Wide neighbor, start of field when exists + static std::optional sample_center( + CenterStarts & new_starts, std::set &done, SupportIslandPoints & results, - const Lines &lines, - std::optional &field_start); + const Lines & lines, + const SampleConfig & config); +private: + /// + /// Check near supports if no exists there add to results + /// + /// Potentional new static point position + /// Results to check near and optionaly append newone + /// When append new support point + /// than fix value of support_in for near new_start + /// Parameters for sampling, + /// minimal_support_distance - search distance in VD + /// max_distance - for fix new_start + static void create_sample_center_end( + const VoronoiGraph::Position &position, + SupportIslandPoints & results, + CenterStarts & new_starts, + const SampleConfig & config); + +public : /// /// DTO represents Wide parts of island to sample /// extend polygon with information about source lines @@ -304,6 +368,7 @@ private: // debug draw functions public : + static bool is_visualization_disabled(); static void draw(SVG & svg, const Field &field, bool draw_border_line_indexes = false, diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp index 26d9c712fe..0fb88fd216 100644 --- a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.cpp @@ -45,7 +45,8 @@ coord_t SupportIslandPoint::move(const Point &destination) std::string SupportIslandPoint::to_string(const Type &type) { - static std::map type_to_tring= + static const std::string undefined = "UNDEFINED"; + static std::map type_to_string= {{Type::one_center_point, "one_center_point"}, {Type::two_points,"two_points"}, {Type::center_line, "center_line"}, @@ -59,8 +60,8 @@ std::string SupportIslandPoint::to_string(const Type &type) {Type::outline, "outline"}, {Type::inner, "inner"}, {Type::undefined, "undefined"}}; - auto it = type_to_tring.find(type); - if (it == type_to_tring.end()) return "UNDEFINED"; + auto it = type_to_string.find(type); + if (it == type_to_string.end()) return undefined; return it->second; } diff --git a/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp b/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp index 23d9d5507c..ccdc728dee 100644 --- a/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VectorUtils.hpp @@ -27,6 +27,8 @@ public: static void sort_by(std::vector &data, std::function &calc) { assert(!data.empty()); + if (data.size() <= 1) return; + // initialize original index locations std::vector idx(data.size()); iota(idx.begin(), idx.end(), 0); diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp index bbfe6298c2..63c8629343 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp @@ -196,6 +196,15 @@ struct VoronoiGraph::Position {} Position(): neighbor(nullptr), ratio(0.) {} + + coord_t calc_distance() const { + return static_cast(neighbor->length() * ratio); + } + + coord_t calc_rest_distance() const + { + return static_cast(neighbor->length() * (1. - ratio)); + } }; } // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index 179fb0042a..a71d01b7c5 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -255,7 +255,8 @@ Slic3r::Polygon VoronoiGraphUtils::to_polygon(const VD::cell_type & cell, lines.push_back(*line); } while ((edge = edge->next()) && edge != cell.incident_edge()); assert(!lines.empty()); - LineUtils::sort_CCW(lines, center); + if (lines.size() > 1) + LineUtils::sort_CCW(lines, center); // preccission to decide when not connect neighbor points double min_distance = maximal_distance / 1000.; size_t count_point = 6; // count added points @@ -865,17 +866,17 @@ VoronoiGraph::ExPath VoronoiGraphUtils::create_longest_path( return longest_path; } -const VoronoiGraph::Node::Neighbor *VoronoiGraphUtils::get_twin(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 &twin_neighbor : neighbor->node->neighbors) { + auto twin_edge = neighbor.edge->twin(); + 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) +const VoronoiGraph::Node *VoronoiGraphUtils::get_twin_node(const VoronoiGraph::Node::Neighbor &neighbor) { return get_twin(neighbor)->node; } @@ -919,13 +920,16 @@ 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( +VoronoiGraph::Position VoronoiGraphUtils::get_position_with_width( 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 + // !!! NOT TRUE !!! + // Only same distance from point and line !!! + // TODO: Fix it return result; } assert(edge->is_finite()); @@ -1014,7 +1018,7 @@ VoronoiGraph::Position VoronoiGraphUtils::align( const VoronoiGraph::Node::Neighbor* neighbor = position.neighbor; double from_distance = neighbor->length() * position.ratio; if (from_distance < max_distance) { - const VoronoiGraph::Node *from_node = VoronoiGraphUtils::get_twin_node(neighbor); + const VoronoiGraph::Node *from_node = VoronoiGraphUtils::get_twin_node(*neighbor); process.emplace(from_node, from_distance); } double to_distance = neighbor->length() * (1 - position.ratio); @@ -1024,7 +1028,7 @@ VoronoiGraph::Position VoronoiGraphUtils::align( } if (process.empty()) { const VoronoiGraph::Node *node = (position.ratio < 0.5) ? - VoronoiGraphUtils::get_twin_node(neighbor) : neighbor->node; + VoronoiGraphUtils::get_twin_node(*neighbor) : neighbor->node; process.emplace(node, max_distance); } @@ -1196,6 +1200,56 @@ void VoronoiGraphUtils::draw(SVG & svg, } } +void VoronoiGraphUtils::for_neighbor_at_distance( + const VoronoiGraph::Position & position, + coord_t max_distance, + std::function fnc) +{ + coord_t act_distance = position.calc_distance(); + const VoronoiGraph::Node *act_node = position.neighbor->node; + const VoronoiGraph::Node *twin_node = get_twin_node(*position.neighbor); + std::set done; + done.insert(twin_node); + done.insert(act_node); + std::queue> process; + coord_t distance = position.calc_rest_distance(); + if (distance < max_distance) + process.push({twin_node, distance}); + + while (true) { + const VoronoiGraph::Node *next_node = nullptr; + coord_t next_distance = 0; + for (const auto &neighbor : act_node->neighbors) { + if (done.find(neighbor.node) != done.end()) + continue; // already checked + done.insert(neighbor.node); + + fnc(neighbor, act_distance); + + coord_t length = static_cast(neighbor.length()); + coord_t distance = act_distance + length; + if (distance >= max_distance) continue; + if (next_node == nullptr) { + next_node = neighbor.node; + next_distance = distance; + } else { + process.push({neighbor.node, distance}); + } + } + if (next_node != nullptr) { // exist next node + act_node = next_node; + act_distance = next_distance; + } else if (!process.empty()) { // exist next process + act_node = process.front().first; + act_distance = process.front().second; + process.pop(); + } else { // no next node neither process + break; + } + } + +} + void VoronoiGraphUtils::draw(SVG & svg, const VD::edge_type &edge, const Lines & lines, diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp index 6555e6253e..4d2ad5f2f9 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp @@ -98,7 +98,7 @@ public: static bool is_point_in_limits(const VD::vertex_type *vertex, const Point & source, double max_distance); - +private: /// /// PRIVATE: function to help convert edge without vertex to line /// @@ -109,13 +109,14 @@ public: static Line create_line_between_source_points( const Point &point1, const Point &point2, double maximal_distance); +public: /// /// Convert edge to line /// only for line edge /// crop infinite edge by maximal distance from source point /// inspiration in VoronoiVisualUtils::clip_infinite_edge /// - /// + /// VD edge /// Source point for voronoi diagram /// Maximal distance from source point /// Croped line, when all line segment is out of max distance return empty optional @@ -358,14 +359,14 @@ public: /// /// neighbor /// Twin neighbor - static const VoronoiGraph::Node::Neighbor *get_twin(const VoronoiGraph::Node::Neighbor *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 *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 @@ -391,7 +392,7 @@ public: /// Specify place on edge /// Source lines for voronoi diagram /// Position on given edge - static VoronoiGraph::Position get_position_with_distance( + static VoronoiGraph::Position get_position_with_width( const VoronoiGraph::Node::Neighbor *neighbor, coord_t width, const Lines & lines); @@ -453,6 +454,17 @@ public: /// True when neighbor node has only one neighbor static bool is_last_neighbor(const VoronoiGraph::Node::Neighbor *neighbor); + /// + /// Loop over neighbor in max distance from position + /// + /// Start of loop + /// Termination of loop + /// function to process neighbor with actual distance + static void for_neighbor_at_distance( + const VoronoiGraph::Position &position, + coord_t max_distance, + std::function fnc); + public: // draw function for debug static void draw(SVG & svg, const VoronoiGraph &graph, diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 40201a3c5e..3f78021e5e 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -460,7 +460,8 @@ SampleConfig create_sample_config(double size) { 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_distance_from_outline = cfg.head_radius; + cfg.maximal_distance_from_outline = cfg.half_distance; cfg.min_side_branch_length = 2 * cfg.minimal_distance_from_outline; cfg.minimal_support_distance = cfg.minimal_distance_from_outline + cfg.half_distance; cfg.max_length_for_one_support_point = 2*size; @@ -469,7 +470,7 @@ SampleConfig create_sample_config(double size) { cfg.min_width_for_outline_support = cfg.max_width_for_center_support_line; cfg.outline_sample_distance = cfg.max_distance; - cfg.minimal_move = std::max(1000., size/1000); + cfg.minimal_move = std::max(10000., size/40); cfg.count_iteration = 100; cfg.max_align_distance = 0; return cfg; @@ -587,12 +588,7 @@ TEST_CASE("Small islands should be supported in center", "[SupGen], [VoronoiSkel SampleConfig cfg = create_sample_config(size); ExPolygons islands = createTestIslands(size); - // TODO: remove next 4 lines, debug sharp triangle - /*auto triangle = PolygonUtils::create_isosceles_triangle(8. * size, 40. * size); - islands = {ExPolygon(triangle)}; - auto test_island = create_tiny_wide_test_2(3 * size, 2 / 3. * size); - islands = {test_island};*/ - + islands = {islands[16]}; for (ExPolygon &island : islands) { // information for debug which island cause problem [[maybe_unused]] size_t debug_index = &island - &islands.front(); @@ -712,6 +708,43 @@ TEST_CASE("Compare sampling test", "[hide]") } } +// source: https://en.wikipedia.org/wiki/Centroid +Slic3r::Point centroid(const Slic3r::Polygon &polygon) { + double sum_x = 0.; + double sum_y = 0.; + double signed_area = 0.; + auto add = [&](const Point &p1, const Point &p2) { + Vec2d p1d = p1.cast(); + double area = p1d.x() * p2.y() - p1d.y() * p2.x(); + sum_x += (p1d.x() + p2.x()) * area; + sum_y += (p1d.y() + p2.y()) * area; + signed_area += area; + }; + const Points &points = polygon.points; + for (size_t i = 1; i < points.size(); i++) { + add(points[i - 1], points[i]); + } + add(points.back(), points.front()); + double area6 = signed_area * 3; + return Point(sum_x / area6, sum_y / area6); +} + +TEST_CASE("Trapezoid centroid should be inside of trapezoid", "[Utils]") +{ + Slic3r::Polygon polygon({ + Point(4702134, 1124765853), + Point(-4702134, 1124765853), + Point(-9404268, 1049531706), + Point(9404268, 1049531706) + }); + + Point my_centroid = centroid(polygon); + CHECK(polygon.contains(my_centroid)); + + Point centroid = polygon.centroid(); + CHECK(polygon.contains(centroid)); +} + #include TEST_CASE("Reorder destructive", "[Utils]"){ std::vector data {0, 1, 3, 2, 4, 7, 6, 5, 8}; @@ -773,5 +806,6 @@ TEST_CASE("Disable visualization", "[hide]") #ifdef STORE_SAMPLE_INTO_SVG_FILES CHECK(false); #endif // STORE_SAMPLE_INTO_SVG_FILES + CHECK(SampleIslandUtils::is_visualization_disabled()); }