mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 13:42:01 +08:00
add sample center circles
This commit is contained in:
parent
fb050136ef
commit
dd61478fd6
@ -2,26 +2,37 @@
|
||||
#define slic3r_SLA_SuppotstIslands_SampleConfig_hpp_
|
||||
|
||||
namespace Slic3r::sla {
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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_
|
||||
|
73
src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp
Normal file
73
src/libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_
|
||||
#define slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_
|
||||
|
||||
#include <libslic3r/sla/SupportPointGenerator.hpp>
|
||||
|
||||
namespace Slic3r::sla {
|
||||
|
||||
/// <summary>
|
||||
/// Factory to create configuration
|
||||
/// </summary>
|
||||
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_
|
393
src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp
Normal file
393
src/libslic3r/SLA/SupportIslands/SampleIslandUtils.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
#include "SampleIslandUtils.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <libslic3r/VoronoiOffset.hpp>
|
||||
#include "IStackFunction.hpp"
|
||||
#include "EvaluateNeighbor.hpp"
|
||||
#include "ParabolaUtils.hpp"
|
||||
#include "VoronoiGraphUtils.hpp"
|
||||
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <libslic3r/VoronoiVisualUtils.hpp>
|
||||
|
||||
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<size_t>(
|
||||
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<std::set<const VoronoiGraph::Node *>> create_circles_sets(
|
||||
const std::vector<VoronoiGraph::Circle> & circles,
|
||||
const VoronoiGraph::ExPath::ConnectedCircles &connected_circle)
|
||||
{
|
||||
std::vector<std::set<const VoronoiGraph::Node *>> result;
|
||||
std::vector<bool> 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<const VoronoiGraph::Node *> 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<const VoronoiGraph::Node *>& 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<NodeDistance> 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<const VoronoiGraph::Node *, double> 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<const VoronoiGraph::Node *, double> 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<size_t>(
|
||||
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<std::set<const VoronoiGraph::Node *>> 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);
|
||||
}
|
||||
}
|
||||
}
|
119
src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp
Normal file
119
src/libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp
Normal file
@ -0,0 +1,119 @@
|
||||
#ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
|
||||
#define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
|
||||
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <libslic3r/SVG.hpp>
|
||||
|
||||
#include "VoronoiGraph.hpp"
|
||||
#include "Parabola.hpp"
|
||||
#include "SampleConfig.hpp"
|
||||
#include "SupportIslandPoint.hpp"
|
||||
|
||||
namespace Slic3r::sla {
|
||||
|
||||
/// <summary>
|
||||
/// Utils class with only static function
|
||||
/// Function for sampling island by Voronoi Graph.
|
||||
/// </summary>
|
||||
class SampleIslandUtils
|
||||
{
|
||||
public:
|
||||
SampleIslandUtils() = delete;
|
||||
|
||||
/// <summary>
|
||||
/// Find point lay on path with distance from first point on path
|
||||
/// </summary>
|
||||
/// <param name="path">Neighbor connected Nodes</param>
|
||||
/// <param name="distance">Distance to final point</param>
|
||||
/// <returns>Points with distance to first node</returns>
|
||||
static Point get_point_on_path(const VoronoiGraph::Nodes &path,
|
||||
double distance);
|
||||
|
||||
/// <summary>
|
||||
/// Find point lay in center of path
|
||||
/// Distance from this point to front of path
|
||||
/// is same as distance to back of path
|
||||
/// </summary>
|
||||
/// <param name="path">Queue of neighbor nodes.(must be neighbor)</param>
|
||||
/// <param name="path_length">length of path</param>
|
||||
/// <returns>Point laying on voronoi diagram</returns>
|
||||
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<const VoronoiGraph::Node *> &circle_set,
|
||||
const VoronoiGraph::Nodes & path_nodes,
|
||||
const CenterLineConfiguration & cfg);
|
||||
|
||||
/// <summary>
|
||||
/// Decide how to sample path
|
||||
/// </summary>
|
||||
/// <param name="path">Path inside voronoi diagram with side branches and circles</param>
|
||||
/// <param name="config">Definition how to sample</param>
|
||||
/// <returns>Support points for island</returns>
|
||||
static SupportIslandPoints sample_expath(
|
||||
const VoronoiGraph::ExPath &path,
|
||||
const SampleConfig &config
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Sample voronoi skeleton
|
||||
/// </summary>
|
||||
/// <param name="graph">Inside skeleton of island</param>
|
||||
/// <param name="config">Params for sampling</param>
|
||||
/// <param name="longest_path">OUTPUT: longest path in graph</param>
|
||||
/// <returns>Vector of sampled points or Empty when distance from edge is
|
||||
/// bigger than max_distance</returns>
|
||||
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_
|
34
src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp
Normal file
34
src/libslic3r/SLA/SupportIslands/SupportIslandPoint.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_
|
||||
#define slic3r_SLA_SuppotstIslands_SupportIslandPoint_hpp_
|
||||
|
||||
#include <libslic3r/Point.hpp>
|
||||
|
||||
namespace Slic3r::sla {
|
||||
|
||||
/// <summary>
|
||||
/// DTO position with information about source of support point
|
||||
/// </summary>
|
||||
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<SupportIslandPoint>;
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
#endif // slic3r_SLA_SuppotstIslands_SupportIslandPoint_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)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -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<Slic3r::Point> 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<Point> points;
|
||||
points.push_back(get_offseted_point(*longest_path.nodes.front(), config.start_distance));
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
std::vector<Slic3r::Point> 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<const VoronoiGraph::Node *> done;
|
||||
std::queue<const VoronoiGraph::Node *> 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,
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "VoronoiGraph.hpp"
|
||||
#include "Parabola.hpp"
|
||||
#include "SampleConfig.hpp"
|
||||
#include "SupportIslandPoint.hpp"
|
||||
|
||||
namespace Slic3r::sla {
|
||||
|
||||
@ -190,48 +191,17 @@ public:
|
||||
/// </summary>
|
||||
static Point get_edge_point(const VD::edge_type *edge, double ratio);
|
||||
|
||||
/// <summary>
|
||||
/// Find point lay on path with distance from first point on path
|
||||
/// </summary>
|
||||
/// <param name="path">Neighbor connected Nodes</param>
|
||||
/// <param name="distance">Distance to final point</param>
|
||||
/// <returns>Points with distance to first node</returns>
|
||||
static Point get_point_on_path(const VoronoiGraph::Nodes &path, double distance);
|
||||
static const VoronoiGraph::Node *getFirstContourNode(
|
||||
const VoronoiGraph &graph);
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="path">Queue of neighbor nodes.(must be neighbor)</param>
|
||||
/// <param name="path_length">length of path</param>
|
||||
/// <returns>Point laying on voronoi diagram</returns>
|
||||
static Point get_center_of_path(const VoronoiGraph::Nodes &path, double path_length)
|
||||
{ return get_point_on_path(path, path_length / 2); }
|
||||
|
||||
/// <summary>
|
||||
/// decide how to sample longest path
|
||||
/// </summary>
|
||||
/// <param name="longest_path">Path inside voronoi diagram with all side branches and circles</param>
|
||||
/// <param name="config">Definition how to sample</param>
|
||||
/// <returns>Support points for island</returns>
|
||||
static std::vector<Point> sample_longest_path(
|
||||
const VoronoiGraph::ExPath &longest_path,
|
||||
const SampleConfig &config
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Sample voronoi skeleton
|
||||
/// </summary>
|
||||
/// <param name="graph">Inside skeleton of island</param>
|
||||
/// <param name="config">Params for sampling</param>
|
||||
/// <param name="longest_path">OUTPUT: longest path in graph</param>
|
||||
/// <returns>Vector of sampled points or Empty when distance from edge is
|
||||
/// bigger than max_distance</returns>
|
||||
static std::vector<Point> sample_voronoi_graph(
|
||||
const VoronoiGraph & graph,
|
||||
const SampleConfig & config,
|
||||
VoronoiGraph::ExPath &longest_path);
|
||||
/// <param name="longest_path">Input point to voronoi graph</param>
|
||||
/// <returns>Maximal widht in graph</returns>
|
||||
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);
|
||||
|
@ -4,13 +4,16 @@
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/BoundingBox.hpp>
|
||||
#include <libslic3r/SLA/SpatIndex.hpp>
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
|
||||
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
|
||||
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp>
|
||||
#include <libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp>
|
||||
|
||||
#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<float> grid({0.1f});
|
||||
std::vector<ExPolygons> 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<float> grid({0.1f});
|
||||
std::vector<ExPolygons> slices;
|
||||
slicer.slice(grid, SlicingMode::Regular, 0.05f, &slices, [] {});
|
||||
ExPolygon frog_leg = slices.front()[1];
|
||||
result.push_back(frog_leg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Point> 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<int>(floor(size.x() / distance))+1;
|
||||
int cols2 = static_cast<int>(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<int>(floor(size.y() / h)) +1;
|
||||
int rows_2 = rows / 2;
|
||||
size_t count_points = rows_2 * (cols1 + static_cast<size_t>(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<double>(size.x());
|
||||
double y_max = offset.y() + static_cast<double>(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<double> 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 <libslic3r/Geometry.hpp>
|
||||
#include <libslic3r/VoronoiOffset.hpp>
|
||||
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<ExPolygons> 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user