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