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