diff --git a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp index 3d03e6a309..75c65d47fe 100644 --- a/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp +++ b/src/libslic3r/SLA/SupportIslands/SampleConfig.hpp @@ -2,26 +2,37 @@ #define slic3r_SLA_SuppotstIslands_SampleConfig_hpp_ namespace Slic3r::sla { - /// -/// Configuration fro sampling voronoi diagram for support point generator +/// Configuration DTO +/// Define where is neccessary to put support point on island +/// Mainly created by SampleConfigFactory /// struct SampleConfig { - // Maximal distance from edge - double max_distance = 1.; // must be bigger than zero - // Maximal distance between samples on skeleton - double sample_size = 1.; // must be bigger than zero - // distance from edge of skeleton - double start_distance = 0; // support head diameter + // Every point on island has at least one support point in maximum distance + // MUST be bigger than zero + double max_distance = 1.; - // 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. - // suggestion 1: Smaller than 2 * SampleConfig.start_distance - // suggestion 2: Bigger than 2 * Head diameter + // Support point head radius + // MUST be bigger than zero + double head_radius = 1; + + // 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.; + + // 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.; -}; + // Maximal length of island supported by 2 points + double 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.; + +}; } // 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 new file mode 100644 index 0000000000..2ab08e3844 --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp @@ -0,0 +1,73 @@ +#ifndef slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_ +#define slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_ + +#include + +namespace Slic3r::sla { + +/// +/// Factory to create configuration +/// +class SampleConfigFactory +{ +public: + SampleConfigFactory() = delete; + + // factory method to iniciate config + static SampleConfig create(const SupportPointGenerator::Config &config) + { + SampleConfig result; + result.max_distance = 100. * config.head_diameter; + result.head_radius = config.head_diameter / 2; + result.minimal_distance_from_outline = config.head_diameter / 2.; + + result.max_length_for_one_support_point = + 2 * result.minimal_distance_from_outline + + config.head_diameter; + double 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 = + 2 * config.head_diameter + + 2 * result.minimal_distance_from_outline; + if (result.max_length_for_one_support_point < min_length_for_one_support_point) + result.max_length_for_one_support_point = min_length_for_one_support_point; + + 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 = + 2 * result.max_distance + + 2 * config.head_diameter + + 2 * result.minimal_distance_from_outline; + if (result.max_length_for_two_support_points > max_length_for_two_support_points) + 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 = + 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; + + 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); + + return result; + } +}; + +} // namespace Slic3r::sla +#endif // slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp new file mode 100644 index 0000000000..2eeed558eb --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp @@ -0,0 +1,393 @@ +#include "SampleIslandUtils.hpp" + +#include +#include +#include "IStackFunction.hpp" +#include "EvaluateNeighbor.hpp" +#include "ParabolaUtils.hpp" +#include "VoronoiGraphUtils.hpp" + +#include +#include + +using namespace Slic3r::sla; + +Slic3r::Point SampleIslandUtils::get_point_on_path( + const VoronoiGraph::Nodes &path, double distance) +{ + const VoronoiGraph::Node *prev_node = nullptr; + double 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); + actual_distance += neighbor->edge_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 ratio = 1. - over_ratio; + return VoronoiGraphUtils::get_edge_point(neighbor->edge, ratio); + } + prev_node = node; + } + // distance must be inside path + // this means bad input params + assert(false); + return Point(0, 0); +} + +SupportIslandPoints SampleIslandUtils::create_side_points( + const VoronoiGraph::Nodes &path, double side_distance) +{ + VoronoiGraph::Nodes reverse_path = path; // copy + std::reverse(reverse_path.begin(), reverse_path.end()); + return { + {get_point_on_path(path, side_distance), SupportIslandPoint::Type::two_points}, + {get_point_on_path(reverse_path, side_distance), SupportIslandPoint::Type::two_points} + }; +} + +SupportIslandPoints SampleIslandUtils::sample_side_branch( + const VoronoiGraph::Node * first_node, + const VoronoiGraph::Path side_path, + double start_offset, + const CenterLineConfiguration &cfg) +{ + assert(cfg.max_sample_distance > start_offset); + double distance = cfg.max_sample_distance - start_offset; + double length = side_path.length - cfg.side_distance - distance; + if (length < 0.) { + VoronoiGraph::Nodes reverse_path = side_path.nodes; + std::reverse(reverse_path.begin(), reverse_path.end()); + reverse_path.push_back(first_node); + return {get_point_on_path(reverse_path, cfg.side_distance)}; + } + // count of segment between points on main path + size_t segment_count = static_cast( + std::ceil(length / cfg.max_sample_distance)); + double sample_distance = length / segment_count; + SupportIslandPoints result; + result.reserve(segment_count + 1); + const VoronoiGraph::Node *prev_node = first_node; + for (const VoronoiGraph::Node *node : side_path.nodes) { + const VoronoiGraph::Node::Neighbor *neighbor = + VoronoiGraphUtils::get_neighbor(prev_node, node); + auto side_item = cfg.branches_map.find(node); + if (side_item != cfg.branches_map.end()) { + double start_offset = (distance < sample_distance / 2.) ? + distance : + (sample_distance - distance); + + if (side_item->second.top().length > cfg.min_length) { + auto side_samples = sample_side_branches(side_item, + start_offset, cfg); + result.insert(result.end(), side_samples.begin(), + side_samples.end()); + } + } + while (distance < neighbor->edge_length) { + double edge_ratio = distance / neighbor->edge_length; + result.emplace_back(VoronoiGraphUtils::get_edge_point(neighbor->edge, edge_ratio), + SupportIslandPoint::Type::center_line); + distance += sample_distance; + } + distance -= neighbor->edge_length; + prev_node = node; + } + assert(fabs(distance - (sample_distance - cfg.side_distance)) < 1e-5); + return result; +} + +SupportIslandPoints SampleIslandUtils::sample_side_branches( + const VoronoiGraph::ExPath::SideBranchesMap::const_iterator + & side_branches_iterator, + double start_offset, + const CenterLineConfiguration &cfg) +{ + const VoronoiGraph::ExPath::SideBranches &side_branches = + side_branches_iterator->second; + const VoronoiGraph::Node *first_node = side_branches_iterator->first; + if (side_branches.size() == 1) + return sample_side_branch(first_node, side_branches.top(), + start_offset, cfg); + + SupportIslandPoints result; + VoronoiGraph::ExPath::SideBranches side_branches_cpy = side_branches; + while (side_branches_cpy.top().length > cfg.min_length) { + auto samples = sample_side_branch(first_node, side_branches_cpy.top(), + start_offset, cfg); + result.insert(result.end(), samples.begin(), samples.end()); + side_branches_cpy.pop(); + } + return result; +} + +std::vector> create_circles_sets( + const std::vector & circles, + const VoronoiGraph::ExPath::ConnectedCircles &connected_circle) +{ + std::vector> result; + std::vector done_circle(circles.size(), false); + for (size_t circle_index = 0; circle_index < circles.size(); + ++circle_index) { + if (done_circle[circle_index]) continue; + done_circle[circle_index] = true; + std::set circle_nodes; + const VoronoiGraph::Circle & circle = circles[circle_index]; + for (const VoronoiGraph::Node *node : circle.nodes) + circle_nodes.insert(node); + + circle_nodes.insert(circle.nodes.begin(), circle.nodes.end()); + auto cc = connected_circle.find(circle_index); + if (cc != connected_circle.end()) { + for (const size_t &cc_index : cc->second) { + done_circle[cc_index] = true; + const VoronoiGraph::Circle &circle = circles[cc_index]; + circle_nodes.insert(circle.nodes.begin(), circle.nodes.end()); + } + } + result.push_back(circle_nodes); + } + return result; +} + +SupportIslandPoints SampleIslandUtils::sample_center_line( + const VoronoiGraph::ExPath &path, const CenterLineConfiguration &cfg) +{ + const VoronoiGraph::Nodes &nodes = path.nodes; + // like side branch separate first node from path + VoronoiGraph::Path main_path({nodes.begin() + 1, nodes.end()}, + path.length); + double start_offset = cfg.max_sample_distance - cfg.side_distance; + SupportIslandPoints result = sample_side_branch(nodes.front(), main_path, + start_offset, cfg); + + if (path.circles.empty()) return result; + SupportIslandPoints result_circles = sample_center_circles(path, cfg); + result.insert(result.end(), result_circles.begin(), result_circles.end()); + return result; +} + +SupportIslandPoints SampleIslandUtils::sample_center_circle( + const std::set& circle_set, + const VoronoiGraph::Nodes& path_nodes, + const CenterLineConfiguration & cfg + ) +{ + SupportIslandPoints result; + // DTO store information about distance to nearest support point + // and path from start point + struct NodeDistance + { + VoronoiGraph::Nodes nodes; // from act node to start + double distance_from_support_point; + NodeDistance(const VoronoiGraph::Node *node, + double distance_from_support_point) + : nodes({node}) + , distance_from_support_point(distance_from_support_point) + {} + }; + // depth search + std::stack process; + + // TODO: propagate distance from support point + // distance from nearest support point + double path_distance = cfg.max_sample_distance / 2; + // path can't be stored in done because it will skip begining of path + std::map path_distances; + // path_nodes are already sampled + for (const VoronoiGraph::Node *node : path_nodes) { + // contain + if (circle_set.find(node) != circle_set.end()) { + process.push(NodeDistance(node, path_distance)); + path_distances[node] = path_distance; + } + } + + // TODO: sample circles out of main path + if (process.empty()) { // TODO: find side branch + } + + // when node is sampled in all side branches. + // Value is distance to nearest support point + std::map done_distance; + while (!process.empty()) { + NodeDistance nd = process.top(); // copy + process.pop(); + const VoronoiGraph::Node *node = nd.nodes.front(); + const VoronoiGraph::Node *prev_node = + (nd.nodes.size() > 1) ? nd.nodes[1] : nullptr; + auto done_distance_item = done_distance.find(node); + if (done_distance_item != done_distance.end()) { + if (done_distance_item->second > nd.distance_from_support_point) + done_distance_item->second = nd.distance_from_support_point; + continue; + } + done_distance[node] = nd.distance_from_support_point; + bool is_node_on_path = (path_distances.find(node) != path_distances.end()); + double &node_distance = done_distance[node]; // append to done node + for (const auto &neighbor : node->neighbors) { + if (neighbor.node == prev_node) continue; + if (circle_set.find(neighbor.node) == circle_set.end()) continue; // out of circle points + auto path_distance_item = path_distances.find(neighbor.node); + bool is_neighbor_on_path = (path_distance_item != path_distances.end()); + if (is_node_on_path && is_neighbor_on_path) continue; // already sampled + auto done_item = done_distance.find(neighbor.node); + bool is_done = done_item != done_distance.end(); + if (is_done || is_neighbor_on_path) { + double &done_distance = (is_done)? done_item->second : path_distance_item->second; + double distance = done_distance + + nd.distance_from_support_point + + neighbor.edge_length; + if (distance < cfg.max_sample_distance) continue; + size_t count_supports = static_cast( + std::floor(distance / cfg.max_sample_distance)); + // distance between support points + double distance_between = distance / (count_supports + 1); + if (distance_between < done_distance) { + // point is calculated to be in done path, SP will be on edge point + result.emplace_back( + VoronoiGraphUtils::get_edge_point(neighbor.edge, 1.), + SupportIslandPoint::Type::center_circle_end); + if (node_distance > neighbor.edge_length) + node_distance = neighbor.edge_length; + done_distance = 0.; + if (count_supports == 1) continue; + count_supports -= 1; + distance -= done_distance; + distance_between = distance / (count_supports + 1); + } + VoronoiGraph::Nodes nodes = nd.nodes; // copy, could be more neighbor + nodes.insert(nodes.begin(), neighbor.node); + for (int i = 1; i <= count_supports; ++i) { + double distance_from_neighbor = + i * (distance_between) -done_distance; + result.emplace_back( + get_point_on_path(nodes, distance_from_neighbor), + SupportIslandPoint::Type::center_circle_end2); + double distance_to_node = neighbor.edge_length - + distance_from_neighbor; + if (distance_to_node > 0. && + node_distance > distance_to_node) + node_distance = distance_to_node; + } + + continue; + } + + NodeDistance next_nd = nd; // copy + next_nd.nodes.insert(next_nd.nodes.begin(), neighbor.node); + next_nd.distance_from_support_point += neighbor.edge_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; + result.emplace_back(VoronoiGraphUtils::get_edge_point(neighbor.edge, ratio), + SupportIslandPoint::Type::center_circle); + next_nd.distance_from_support_point -= cfg.max_sample_distance; + } + process.push(next_nd); + } + } + return result; +} + +SupportIslandPoints SampleIslandUtils::sample_center_circles( + const VoronoiGraph::ExPath &path, const CenterLineConfiguration &cfg) +{ + // vector of connected circle points + // for detection path from circle + std::vector> circles_sets = + create_circles_sets(path.circles, path.connected_circle); + if (circles_sets.size() == 1) + return sample_center_circle(circles_sets.front(), path.nodes, cfg); + + SupportIslandPoints result; + for (const auto &circle_set : circles_sets) { + SupportIslandPoints circle_result = sample_center_circle(circle_set, path.nodes, cfg); + result.insert(result.end(), circle_result.begin(), circle_result.end()); + } + return result; +} + +SupportIslandPoints SampleIslandUtils::sample_expath( + const VoronoiGraph::ExPath &path, const SampleConfig &config) +{ + // 1) One support point + if (path.length < config.max_length_for_one_support_point) { + // create only one point in center + Point p = get_center_of_path(path.nodes, path.length); + SupportIslandPoint supportIslandPoint(p, SupportIslandPoint::Type::one_center_point); + return {supportIslandPoint}; + } + + double max_width = VoronoiGraphUtils::get_max_width(path); + if (max_width < config.max_width_for_center_supportr_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); + + // othewise sample path + CenterLineConfiguration + centerLineConfiguration(path.side_branches, + 2 * config.minimal_distance_from_outline, + config.max_distance, + config.minimal_distance_from_outline); + + return sample_center_line(path, centerLineConfiguration); + } + + // 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 + + SupportIslandPoints points; + points.push_back(VoronoiGraphUtils::get_offseted_point( + *path.nodes.front(), config.minimal_distance_from_outline)); + + return points; +} + +SupportIslandPoints SampleIslandUtils::sample_voronoi_graph( + const VoronoiGraph & graph, + 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); + return sample_expath(longest_path, config); +} + +void SampleIslandUtils::draw(SVG & svg, + const SupportIslandPoints &supportIslandPoints, + double size, + const char * color, + bool write_type) +{ + for (const auto &p : supportIslandPoints) { + svg.draw(p.point, color, size); + if (write_type && p.type != SupportIslandPoint::Type::undefined) { + auto type_name = magic_enum::enum_name(p.type); + Point start = p.point + Point(size, 0.); + svg.draw_text(start, std::string(type_name).c_str(), color); + } + } +} diff --git a/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp new file mode 100644 index 0000000000..4036d8b487 --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp @@ -0,0 +1,119 @@ +#ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ +#define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ + +#include +#include +#include + +#include "VoronoiGraph.hpp" +#include "Parabola.hpp" +#include "SampleConfig.hpp" +#include "SupportIslandPoint.hpp" + +namespace Slic3r::sla { + +/// +/// Utils class with only static function +/// Function for sampling island by Voronoi Graph. +/// +class SampleIslandUtils +{ +public: + SampleIslandUtils() = delete; + + /// + /// Find point lay on path with distance from first point on path + /// + /// Neighbor connected Nodes + /// Distance to final point + /// Points with distance to first node + static Point get_point_on_path(const VoronoiGraph::Nodes &path, + double distance); + + /// + /// Find point lay in center of path + /// Distance from this point to front of path + /// is same as distance to back of path + /// + /// Queue of neighbor nodes.(must be neighbor) + /// length of path + /// Point laying on voronoi diagram + static Point get_center_of_path(const VoronoiGraph::Nodes &path, + double path_length) + { + return get_point_on_path(path, path_length / 2); + } + + // 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); + + // DTO with data for sampling line in center + struct CenterLineConfiguration + { + const VoronoiGraph::ExPath::SideBranchesMap &branches_map; + double min_length; + double max_sample_distance; + double side_distance; + CenterLineConfiguration( + const VoronoiGraph::ExPath::SideBranchesMap &branches_map, + double min_length, + double max_sample_distance, + double side_distance) + : branches_map(branches_map) + , min_length(min_length) + , max_sample_distance(max_sample_distance) + , side_distance(side_distance) + {} + }; + // create points along path and its side branches(recursively) + static SupportIslandPoints sample_side_branch( + const VoronoiGraph::Node * first_node, + const VoronoiGraph::Path side_path, + double start_offset, + const CenterLineConfiguration &cfg); + static SupportIslandPoints sample_side_branches( + const VoronoiGraph::ExPath::SideBranchesMap::const_iterator& side_branches_iterator, + double start_offset, + const CenterLineConfiguration &cfg); + // create points along path and its side branches(recursively) + colve circles + static SupportIslandPoints sample_center_line(const VoronoiGraph::ExPath &path, const CenterLineConfiguration &cfg); + // create point along multi circles in path + static SupportIslandPoints sample_center_circles(const VoronoiGraph::ExPath &path, const CenterLineConfiguration &cfg); + static SupportIslandPoints sample_center_circle( + const std::set &circle_set, + const VoronoiGraph::Nodes & path_nodes, + const CenterLineConfiguration & cfg); + + /// + /// Decide how to sample path + /// + /// Path inside voronoi diagram with side branches and circles + /// Definition how to sample + /// Support points for island + static SupportIslandPoints sample_expath( + const VoronoiGraph::ExPath &path, + const SampleConfig &config + ); + + /// + /// Sample voronoi skeleton + /// + /// Inside skeleton 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 SampleConfig & config, + VoronoiGraph::ExPath &longest_path); + + static void draw(SVG & svg, + const SupportIslandPoints &supportIslandPoints, + double size, + const char *color = "lightgreen", + bool write_type = true); +}; + +} // namespace Slic3r::sla +#endif // slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp new file mode 100644 index 0000000000..af055fb412 --- /dev/null +++ b/src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp @@ -0,0 +1,34 @@ +#ifndef slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_ +#define slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_ + +#include + +namespace Slic3r::sla { + +/// +/// DTO position with information about source of support point +/// +struct SupportIslandPoint +{ + enum class Type : unsigned char { + one_center_point, + two_points, + center_line, + center_circle, + center_circle_end, + center_circle_end2, + undefined + }; + + Slic3r::Point point; // 2 point in layer coordinate + Type type; + + SupportIslandPoint(Slic3r::Point point, Type type = Type::undefined) + : point(std::move(point)), type(type) + {} +}; + +using SupportIslandPoints = std::vector; + +} // namespace Slic3r::sla +#endif // slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_ diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp index 206d057558..4aa6470636 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraph.hpp @@ -62,8 +62,11 @@ struct VoronoiGraph::Node::Neighbor double max_width; public: - Neighbor(const VD::edge_type *edge, const Node *node, double edge_length) - : edge(edge), node(node), edge_length(edge_length) + Neighbor(const VD::edge_type *edge, const Node *node, double edge_length, double max_width) + : edge(edge) + , node(node) + , edge_length(edge_length) + , max_width(max_width) {} }; diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index 66952562e2..cde17364ac 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -113,8 +113,24 @@ double VoronoiGraphUtils::calculate_max_width( Point v1{edge.vertex1()->x(), edge.vertex1()->y()}; if (edge.is_linear()) { - // line is initialized by 2 line segments only - assert(!edge.cell()->contains_point()); + // edge line could be initialized by 2 points + if (edge.cell()->contains_point()) { + const Line &source_line = lines[edge.cell()->source_index()]; + Point source_point; + if (edge.cell()->source_category() == + boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) + source_point = source_line.a; + else { + assert(edge.cell()->source_category() == + boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT); + source_point = source_line.b; + } + Point vec0 = source_point - v0; + Point vec1 = source_point - v1; + double distance0 = sqrt(vec0.x() * vec0.x() + vec0.y() * vec0.y()); + double distance1 = sqrt(vec0.x() * vec0.x() + vec0.y() * vec0.y()); + return 2 * std::max(distance0, distance1); + } assert(edge.cell()->contains_segment()); assert(!edge.twin()->cell()->contains_point()); assert(edge.twin()->cell()->contains_segment()); @@ -175,10 +191,11 @@ VoronoiGraph VoronoiGraphUtils::getSkeleton(const VD &vd, const Lines &lines) 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); + VoronoiGraph::Node::Neighbor neighbor0(&edge, node1, length, max_width); node0->neighbors.push_back(neighbor0); - VoronoiGraph::Node::Neighbor neighbor1(edge.twin(), node0, length); + VoronoiGraph::Node::Neighbor neighbor1(edge.twin(), node0, length, max_width); node1->neighbors.push_back(neighbor1); } return skeleton; @@ -514,7 +531,8 @@ VoronoiGraph::ExPath VoronoiGraphUtils::create_longest_path( return longest_path; } -Slic3r::Point VoronoiGraphUtils::get_edge_point(const VD::edge_type *edge, double ratio) +Slic3r::Point VoronoiGraphUtils::get_edge_point(const VD::edge_type *edge, + double ratio) { const VD::vertex_type *v0 = edge->vertex0(); const VD::vertex_type *v1 = edge->vertex1(); @@ -524,9 +542,7 @@ Slic3r::Point VoronoiGraphUtils::get_edge_point(const VD::edge_type *edge, doubl return Point(v1->x(), v1->y()); if (edge->is_linear()) { - Point dir( - v1->x() - v0->x(), - v1->y() - v0->y()); + Point dir(v1->x() - v0->x(), v1->y() - v0->y()); // normalize dir *= ratio; return Point(v0->x() + dir.x(), v0->y() + dir.y()); @@ -541,71 +557,83 @@ Slic3r::Point VoronoiGraphUtils::get_edge_point(const VD::edge_type *edge, doubl return Point(v0->x() + dir.x(), v0->y() + dir.y()); } -Slic3r::Point VoronoiGraphUtils::get_point_on_path(const VoronoiGraph::Nodes &path, double distance) +const VoronoiGraph::Node *VoronoiGraphUtils::getFirstContourNode( + const VoronoiGraph &graph) { - const VoronoiGraph::Node *prev_node = nullptr; - double actual_distance = 0.; - for (const VoronoiGraph::Node *node : path) { - if (prev_node == nullptr) { // first call - prev_node = node; - continue; - } - const VoronoiGraph::Node::Neighbor *neighbor = get_neighbor(prev_node, node); - actual_distance += neighbor->edge_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 ratio = 1. - over_ratio; - return get_edge_point(neighbor->edge, ratio); - } - prev_node = node; - } - // distance must be inside path - // this means bad input params - assert(false); - return Point(0, 0); -} - -std::vector VoronoiGraphUtils::sample_longest_path( - const VoronoiGraph::ExPath &longest_path, const SampleConfig &config) -{ - // 1) One support point - if (longest_path.length < - config.max_length_for_one_support_point) { // create only one - // point in center - // sample in center of voronoi - return {get_center_of_path(longest_path.nodes, longest_path.length)}; - } - // 2) Two support points - //if (longest_path.length < config.max_distance) {} - - std::vector points; - points.push_back(get_offseted_point(*longest_path.nodes.front(), config.start_distance)); - - return points; -} - -std::vector VoronoiGraphUtils::sample_voronoi_graph( - const VoronoiGraph & graph, - const SampleConfig & config, - VoronoiGraph::ExPath &longest_path) -{ - // first vertex on contour: - const VoronoiGraph::Node *start_node = nullptr; for (const auto &[key, value] : graph.data) { const VD::vertex_type & vertex = *key; Voronoi::VertexCategory category = Voronoi::vertex_category(vertex); if (category == Voronoi::VertexCategory::OnContour) { - start_node = &value; - break; + return &value; } } - // every island has to have a point on contour - assert(start_node != nullptr); - longest_path = create_longest_path(start_node); - // longest_path = create_longest_path_recursive(start_node); - return sample_longest_path(longest_path, config); + return nullptr; +} + +double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path) +{ + double max = 0.; + const VoronoiGraph::Node *prev_node = nullptr; + for (const VoronoiGraph::Node *node : path) { + if (prev_node == nullptr) { + prev_node = node; + continue; + } + const VoronoiGraph::Node::Neighbor *neighbor = get_neighbor(prev_node, node); + if (max < neighbor->max_width) max = neighbor->max_width; + prev_node = node; + } + return max; +} + +double VoronoiGraphUtils::get_max_width( + const VoronoiGraph::ExPath &longest_path) +{ + double 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 + while (!side_branches.empty()) { + 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); + if (max < max_side_branch) max = max_side_branch; + side_branches.pop(); + } + } + + for (const VoronoiGraph::Circle &circle : longest_path.circles) { + 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)); + if (max < max_circle) max = max_circle; + } + + return max; +} + +// !!! is slower than go along path +double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node) +{ + double max = 0.; + std::set done; + std::queue process; + process.push(node); + while (!process.empty()) { + const VoronoiGraph::Node *actual_node = process.front(); + process.pop(); + if (done.find(actual_node) != done.end()) continue; + 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; + } + done.insert(actual_node); + } + return max; } void VoronoiGraphUtils::draw(SVG & svg, diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp index 79f4e47978..dfc36ffffe 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp @@ -11,6 +11,7 @@ #include "VoronoiGraph.hpp" #include "Parabola.hpp" #include "SampleConfig.hpp" +#include "SupportIslandPoint.hpp" namespace Slic3r::sla { @@ -190,48 +191,17 @@ public: /// static Point get_edge_point(const VD::edge_type *edge, double ratio); - /// - /// Find point lay on path with distance from first point on path - /// - /// Neighbor connected Nodes - /// Distance to final point - /// Points with distance to first node - static Point get_point_on_path(const VoronoiGraph::Nodes &path, double distance); + static const VoronoiGraph::Node *getFirstContourNode( + const VoronoiGraph &graph); /// - /// Find point lay in center of path - /// Distance from this point to front of path - /// is same as distance to back of path + /// Get max width from edge in voronoi graph /// - /// Queue of neighbor nodes.(must be neighbor) - /// length of path - /// Point laying on voronoi diagram - static Point get_center_of_path(const VoronoiGraph::Nodes &path, double path_length) - { return get_point_on_path(path, path_length / 2); } - - /// - /// decide how to sample longest path - /// - /// Path inside voronoi diagram with all side branches and circles - /// Definition how to sample - /// Support points for island - static std::vector sample_longest_path( - const VoronoiGraph::ExPath &longest_path, - const SampleConfig &config - ); - - /// - /// Sample voronoi skeleton - /// - /// Inside skeleton 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 std::vector sample_voronoi_graph( - const VoronoiGraph & graph, - const SampleConfig & config, - VoronoiGraph::ExPath &longest_path); + /// 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); public: // draw function for debug static void draw(SVG &svg, const VoronoiGraph &graph, coord_t width); diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 6766c5a2e6..bec6dcbb03 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -4,13 +4,16 @@ #include #include #include +#include #include #include +#include #include "sla_test_utils.hpp" -namespace Slic3r { namespace sla { +using namespace Slic3r; +using namespace Slic3r::sla; TEST_CASE("Overhanging point should be supported", "[SupGen]") { @@ -135,32 +138,105 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]") REQUIRE(!pts.empty()); } +// all triangle side are same length +Slic3r::Polygon equilateral_triangle(double size) +{ + return {{.0, .0}, + {size, .0}, + {size / 2., sqrt(size * size - size * size / 4)}}; +} + +// two side of triangle are same size +Slic3r::Polygon isosceles_triangle(double side, double height) +{ + return {{-side / 2, 0.}, {side / 2, 0.}, {.0, height}}; +} + +Slic3r::Polygon square(double size) +{ + double size_2 = size / 2; + return {{-size_2, size_2}, + {-size_2, -size_2}, + {size_2, -size_2}, + {size_2, size_2}}; +} + +Slic3r::Polygon rect(double x, double y){ + return {{.0, y}, {.0, .0}, {x, .0}, {x, y}}; +} + +Slic3r::Polygon circle(double radius, size_t count_line_segments) { + // CCW: couter clock wise, CW: clock wise + Points circle; + circle.reserve(count_line_segments); + for (size_t i = 0; i < count_line_segments; ++i) { + double alpha = (2 * M_PI * i) / count_line_segments; + double sina = sin(alpha); + double cosa = cos(alpha); + circle.emplace_back(-radius * sina, radius * cosa); + } + return Slic3r::Polygon(circle); +} + +Slic3r::Polygon create_cross_roads(double size, double width) +{ + auto r1 = rect( 5.3 * size, width); + r1.rotate(3.14/4); + auto r2 = rect(6.1*size, 3/4.*width); + r2.rotate(-3.14 / 5); + auto r3 = rect(7.9*size, 4/5.*width); + r3.translate(Point(-2*size, size)); + auto r4 = rect(5 / 6. * width, 5.7 * size); + r4.translate(Point(size,0.)); + Polygons rr = union_(Polygons({r1, r2, r3, r4})); + return rr.front(); +} + +ExPolygon create_trinagle_with_hole(double size) +{ + return ExPolygon(equilateral_triangle(size), {{size / 4, size / 4}, + {size / 2, size / 2}, + {size / 2, size / 4}}); +} + +ExPolygon create_square_with_hole(double size, double hole_size) +{ + assert(sqrt(hole_size *hole_size / 2) < size); + auto hole = square(hole_size); + hole.rotate(M_PI / 4.); // 45 + hole.reverse(); + return ExPolygon(square(size), hole); +} + +ExPolygon create_square_with_4holes(double size, double hole_size) { + auto hole = square(hole_size); + hole.reverse(); + double size_4 = size / 4; + auto h1 = hole; + h1.translate(size_4, size_4); + auto h2 = hole; + h2.translate(-size_4, size_4); + auto h3 = hole; + h3.translate(size_4, -size_4); + auto h4 = hole; + h4.translate(-size_4, -size_4); + ExPolygon result(square(size)); + result.holes = Polygons({h1, h2, h3, h4}); + return result; +} + +// boudary of circle +ExPolygon create_disc(double radius, double width, size_t count_line_segments) +{ + double width_2 = width / 2; + auto hole = circle(radius-width_2, count_line_segments); + hole.reverse(); + return ExPolygon(circle(radius + width_2, count_line_segments), hole); +} + ExPolygons createTestIslands(double size) { - ExPolygon triangle( - Polygon{{.0, .0}, - {size, .0}, - {size / 2., sqrt(size * size - size * size / 4)}}); - ExPolygon sharp_triangle( - Polygon{{.0, size / 2}, {.0, .0}, {2 * size, .0}}); - ExPolygon triangle_with_hole({{.0, .0}, - {size, .0}, - {size / 2., - sqrt(size * size - size * size / 4)}}, - {{size / 4, size / 4}, - {size / 2, size / 2}, - {size / 2, size / 4}}); - ExPolygon square(Polygon{{.0, size}, {.0, .0}, {size, .0}, {size, size}}); - ExPolygon rect( - Polygon{{.0, size}, {.0, .0}, {2 * size, .0}, {2 * size, size}}); - ExPolygon rect_with_hole({{-size, size}, // rect CounterClockWise - {-size, -size}, - {size, -size}, - {size, size}}, - {{0., size / 2}, // inside rect ClockWise - {size / 2, 0.}, - {0., -size / 2}, - {-size / 2, 0.}}); + bool useFrogLeg = false; // need post reorganization of longest path ExPolygon mountains({{0., 0.}, {size, 0.}, @@ -169,28 +245,6 @@ ExPolygons createTestIslands(double size) {3 * size / 7, 2 * size}, {2 * size / 7, size / 6}, {size / 7, size}}); - ExPolygon rect_with_4_hole(Polygon{{0., size}, // rect CounterClockWise - {0., 0.}, - {size, 0.}, - {size, size}}); - // inside rects ClockWise - double size5 = size / 5.; - rect_with_4_hole.holes = Polygons{{{size5, 4 * size5}, - {2 * size5, 4 * size5}, - {2 * size5, 3 * size5}, - {size5, 3 * size5}}, - {{3 * size5, 4 * size5}, - {4 * size5, 4 * size5}, - {4 * size5, 3 * size5}, - {3 * size5, 3 * size5}}, - {{size5, 2 * size5}, - {2 * size5, 2 * size5}, - {2 * size5, size5}, - {size5, size5}}, - {{3 * size5, 2 * size5}, - {4 * size5, 2 * size5}, - {4 * size5, size5}, - {3 * size5, size5}}}; size_t count_cirlce_lines = 16; // test stack overfrow double r_CCW = size / 2; @@ -207,36 +261,159 @@ ExPolygons createTestIslands(double size) circle_CW.emplace_back(r_CW * sina, r_CW * cosa); } ExPolygon double_circle(circle_CCW, circle_CW); + ExPolygons result = { + // one support point + ExPolygon(equilateral_triangle(size)), + ExPolygon(square(size)), + ExPolygon(rect(size / 2, size)), + ExPolygon(isosceles_triangle(size / 2, 3 * size / 2)), // small sharp triangle + ExPolygon(circle(size/2, 10)), + create_square_with_4holes(size, size / 4), + create_disc(size/4, size / 4, 10), - TriangleMesh mesh = load_model("frog_legs.obj"); - TriangleMeshSlicer slicer{&mesh}; - std::vector grid({0.1f}); - std::vector slices; - slicer.slice(grid, SlicingMode::Regular, 0.05f, &slices, [] {}); - ExPolygon frog_leg = slices.front()[1]; // + // two support points + ExPolygon(isosceles_triangle(size / 2, 3 * size)), // small sharp triangle + ExPolygon(rect(size / 2, 3 * size)), - return { - triangle, square, - sharp_triangle, rect, - rect_with_hole, triangle_with_hole, - rect_with_4_hole, mountains, - double_circle - //, frog_leg + // tiny line support points + ExPolygon(rect(size / 2, 10 * size)), // long line + ExPolygon(create_cross_roads(size, size / 3)), + create_disc(3*size, size / 4, 30), + create_square_with_4holes(5 * size, 5 * size / 2 - size / 3), + + // still problem + // three support points + ExPolygon(equilateral_triangle(3 * size)), + ExPolygon(circle(size, 20)), + + mountains, + create_trinagle_with_hole(size), + create_square_with_hole(size, size / 2), + create_square_with_hole(size, size / 3) }; + + if (useFrogLeg) { + TriangleMesh mesh = load_model("frog_legs.obj"); + TriangleMeshSlicer slicer{&mesh}; + std::vector grid({0.1f}); + std::vector slices; + slicer.slice(grid, SlicingMode::Regular, 0.05f, &slices, [] {}); + ExPolygon frog_leg = slices.front()[1]; + result.push_back(frog_leg); + } + return result; } -std::vector test_island_sampling(const ExPolygon & island, +Points createNet(const BoundingBox& bounding_box, double distance) +{ + Point size = bounding_box.size(); + double distance_2 = distance / 2; + int cols1 = static_cast(floor(size.x() / distance))+1; + int cols2 = static_cast(floor((size.x() - distance_2) / distance))+1; + // equilateral triangle height with side distance + double h = sqrt(distance * distance - distance_2 * distance_2); + int rows = static_cast(floor(size.y() / h)) +1; + int rows_2 = rows / 2; + size_t count_points = rows_2 * (cols1 + static_cast(cols2)); + if (rows % 2 == 1) count_points += cols2; + Points result; + result.reserve(count_points); + bool isOdd = true; + Point offset = bounding_box.min; + double x_max = offset.x() + static_cast(size.x()); + double y_max = offset.y() + static_cast(size.y()); + for (double y = offset.y(); y <= y_max; y += h) { + double x_offset = offset.x(); + if (isOdd) x_offset += distance_2; + isOdd = !isOdd; + for (double x = x_offset; x <= x_max; x += distance) { + result.emplace_back(x, y); + } + } + assert(result.size() == count_points); + return result; +} + +// create uniform triangle net and return points laying inside island +Points rasterize(const ExPolygon &island, double distance) { + BoundingBox bb; + for (const Point &pt : island.contour.points) bb.merge(pt); + Points fullNet = createNet(bb, distance); + Points result; + result.reserve(fullNet.size()); + std::copy_if(fullNet.begin(), fullNet.end(), std::back_inserter(result), + [&island](const Point &p) { return island.contains(p); }); + return result; +} + +SupportIslandPoints test_island_sampling(const ExPolygon & island, const SampleConfig &config) { auto points = SupportPointGenerator::uniform_cover_island(island, config); + Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer + + bool is_ok = true; + double max_distance = config.max_distance; + std::vector point_distances(chck_points.size(), + {max_distance + 1}); + for (size_t index = 0; index < chck_points.size(); ++index) { + const Point &chck_point = chck_points[index]; + double &min_distance = point_distances[index]; + bool exist_close_support_point = false; + for (auto &island_point : points) { + Point& p = island_point.point; + Point abs_diff(fabs(p.x() - chck_point.x()), + fabs(p.y() - chck_point.y())); + if (abs_diff.x() < min_distance && abs_diff.y() < min_distance) { + double distance = sqrt((double) abs_diff.x() * abs_diff.x() + + (double) abs_diff.y() * abs_diff.y()); + if (min_distance > distance) { + min_distance = distance; + exist_close_support_point = true; + }; + } + } + if (!exist_close_support_point) is_ok = false; + } + + if (!is_ok) { // visualize + static int counter = 0; + BoundingBox bb; + for (const Point &pt : island.contour.points) bb.merge(pt); + SVG svg("Error" + std::to_string(++counter) + ".svg", bb); + svg.draw(island, "blue", 0.5f); + for (auto p : points) + svg.draw(p.point, "lightgreen", config.head_radius); + for (size_t index = 0; index < chck_points.size(); ++index) { + const Point &chck_point = chck_points[index]; + double distance = point_distances[index]; + bool isOk = distance < max_distance; + std::string color = (isOk) ? "gray" : "red"; + svg.draw(chck_point, color, config.head_radius / 4); + } + } CHECK(!points.empty()); + //CHECK(is_ok); // all points must be inside of island - for (const auto &point : points) { CHECK(island.contains(point)); } + for (const auto &point : points) { CHECK(island.contains(point.point)); } return points; } +SampleConfig create_sample_config(double size) { + SampleConfig cfg; + cfg.max_distance = 3 * size + 0.1; + cfg.head_radius = size / 4; + cfg.minimal_distance_from_outline = cfg.head_radius + size/10; + 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; + return cfg; +} +#include +#include TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]") { TriangleMesh mesh = load_model("frog_legs.obj"); @@ -245,29 +422,25 @@ TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]") std::vector slices; slicer.slice(grid, SlicingMode::Regular, 0.05f, &slices, [] {}); ExPolygon frog_leg = slices.front()[1]; + SampleConfig cfg = create_sample_config(3e7); - double size = 3e7; - SampleConfig cfg; - cfg.max_distance = size + 0.1; - cfg.sample_size = size / 5; - cfg.start_distance = 0.2 * size; // radius of support head - cfg.max_length_for_one_support_point = 3 * size; - + using VD = Slic3r::Geometry::VoronoiDiagram; + VD vd; + Lines lines = to_lines(frog_leg); + construct_voronoi(lines.begin(), lines.end(), &vd); + Slic3r::Voronoi::annotate_inside_outside(vd, lines); + for (int i = 0; i < 100; ++i) { - auto points = SupportPointGenerator::uniform_cover_island( - frog_leg, cfg); + VoronoiGraph::ExPath longest_path; + VoronoiGraph skeleton = VoronoiGraphUtils::getSkeleton(vd, lines); + auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path); } } TEST_CASE("Small islands should be supported in center", "[SupGen][VoronoiSkeleton]") { - double size = 3e7; - SampleConfig cfg; - cfg.max_distance = size + 0.1; - cfg.sample_size = size / 5; - cfg.start_distance = 0.2 * size; // radius of support head - cfg.max_length_for_one_support_point = 3 * size; - + double size = 3e7; + SampleConfig cfg = create_sample_config(size); ExPolygons islands = createTestIslands(size); for (auto &island : islands) { auto points = test_island_sampling(island, cfg); @@ -275,10 +448,7 @@ TEST_CASE("Small islands should be supported in center", "[SupGen][VoronoiSkelet island.rotate(angle); auto pointsR = test_island_sampling(island, cfg); - for (Point &p : pointsR) p.rotate(-angle); - + //for (Point &p : pointsR) p.rotate(-angle); // points should be equal to pointsR } } - -}} // namespace Slic3r::sla