Create ExPolygon from wide part of island

This commit is contained in:
Filip Sykala 2021-04-06 09:07:43 +02:00 committed by Lukas Matena
parent 85984ca189
commit 51ce8fbd62
12 changed files with 824 additions and 85 deletions

View File

@ -2,6 +2,7 @@
#include <libslic3r/Geometry.hpp>
#include <functional>
#include "VectorUtils.hpp"
#include "PointUtils.hpp"
using namespace Slic3r::sla;
@ -94,7 +95,7 @@ std::optional<Slic3r::Line> LineUtils::crop_half_ray(const Line & half_ray,
fnc use_point_y = [&half_ray, &dir](const Point &p) -> bool {
return (p.y() > half_ray.a.y()) == (dir.y() > 0);
};
bool use_x = abs(dir.x()) > abs(dir.y());
bool use_x = PointUtils::is_majorit_x(dir);
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
@ -117,7 +118,7 @@ std::optional<Slic3r::Linef> LineUtils::crop_half_ray(const Linef & half_ray,
fnc use_point_y = [&half_ray, &dir](const Vec2d &p) -> bool {
return (p.y() > half_ray.a.y()) == (dir.y() > 0);
};
bool use_x = fabs(dir.x()) > fabs(dir.y());
bool use_x = PointUtils::is_majorit_x(dir);
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
@ -143,7 +144,7 @@ std::optional<Slic3r::Line> LineUtils::crop_line(const Line & line,
return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) :
(p.y() < line.a.y()) && (p.y() > line.b.y());
};
bool use_x = abs(dir.x()) > abs(dir.y());
bool use_x = PointUtils::is_majorit_x(dir);
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
@ -182,7 +183,7 @@ std::optional<Slic3r::Linef> LineUtils::crop_line(const Linef & line,
return (dir.y() > 0) ? (p.y() > line.a.y()) && (p.y() < line.b.y()) :
(p.y() < line.a.y()) && (p.y() > line.b.y());
};
bool use_x = abs(dir.x()) > abs(dir.y());
bool use_x = PointUtils::is_majorit_x(dir);
fnc use_point = (use_x) ? use_point_x : use_point_y;
bool use_a = use_point(segment->a);
bool use_b = use_point(segment->b);
@ -254,6 +255,124 @@ double LineUtils::perp_distance(const Linef &line, Vec2d p)
return std::abs(cross2(v, va)) / v.norm();
}
bool LineUtils::is_parallel(const Line &first, const Line &second)
{
Point dir1 = first.b - first.a;
Point dir2 = second.b - second.a;
coord_t cross(
static_cast<int64_t>(dir1.x()) * dir2.y() -
static_cast<int64_t>(dir2.x()) * dir1.y()
);
return (cross == 0);
}
std::optional<Slic3r::Vec2d> LineUtils::intersection(const Line &ray1, const Line &ray2)
{
const Vec2d v1 = direction(ray1).cast<double>();
const Vec2d v2 = direction(ray2).cast<double>();
double denom = cross2(v1, v2);
if (fabs(denom) < std::numeric_limits<float>::epsilon()) return {};
const Vec2d v12 = (ray1.a - ray2.a).cast<double>();
double nume = cross2(v2, v12);
double t = nume / denom;
return (ray1.a.cast<double>() + t * v1);
}
LineUtils::LineConnection LineUtils::create_line_connection(const Slic3r::Lines &lines)
{
LineConnection line_connection;
static const size_t bad_index = -1;
auto insert = [&](size_t line_index, size_t connected, bool connect_by_a){
auto item = line_connection.find(line_index);
if (item == line_connection.end()) {
// create new
line_connection[line_index] = (connect_by_a) ?
std::pair<size_t, size_t>(connected, bad_index) :
std::pair<size_t, size_t>(bad_index, connected);
} else {
std::pair<size_t, size_t> &pair = item->second;
size_t &ref_index = (connect_by_a) ? pair.first : pair.second;
assert(ref_index == bad_index);
ref_index = connected;
}
};
auto inserts = [&](size_t i1, size_t i2)->bool{
bool is_l1_a_connect = true; // false => l1_b_connect
const Slic3r::Line &l1 = lines[i1];
const Slic3r::Line &l2 = lines[i2];
if (!PointUtils::is_equal(l1.a, l2.b)) return false;
if (!PointUtils::is_equal(l1.b, l2.a)) return false;
else is_l1_a_connect = false;
insert(i1, i2, is_l1_a_connect);
insert(i2, i1, !is_l1_a_connect);
return true;
};
std::vector<size_t> not_finished;
size_t prev_index = lines.size() - 1;
for (size_t index = 0; index < lines.size(); ++index) {
if (!inserts(prev_index, index)) {
bool found_index = false;
bool found_prev_index = false;
std::remove_if(not_finished.begin(), not_finished.end(),
[&](const size_t &not_finished_index) {
if (!found_index && inserts(index, not_finished_index)) {
found_index = true;
return true;
}
if (!found_prev_index && inserts(prev_index, not_finished_index)) {
found_prev_index = true;
return true;
}
return false;
});
if (!found_index) not_finished.push_back(index);
if (!found_prev_index) not_finished.push_back(prev_index);
}
prev_index = index;
}
assert(not_finished.empty());
return line_connection;
}
std::map<size_t, size_t> LineUtils::create_line_connection_over_b(const Lines &lines)
{
static const size_t bad_index = -1;
std::map<size_t, size_t> line_connection;
auto inserts = [&](size_t i1, size_t i2) -> bool {
const Line &l1 = lines[i1];
const Line &l2 = lines[i2];
bool is_connected = PointUtils::is_equal(l1.b, l2.a);
if (!PointUtils::is_equal(l1.b, l2.a))
return false;
assert(line_connection.find(i1) == line_connection.end());
line_connection[i1] = i2;
return true;
};
std::vector<size_t> not_finished;
size_t prev_index = lines.size() - 1;
for (size_t index = 0; index < lines.size(); ++index) {
if (!inserts(prev_index, index)) {
bool found = false;
std::remove_if(not_finished.begin(), not_finished.end(),
[&](const size_t &not_finished_index) {
if (!found && inserts(prev_index, not_finished_index)) {
found = true;
return true;
}
return false;
});
if(!found) not_finished.push_back(prev_index);
}
prev_index = index;
}
assert(not_finished.empty());
return line_connection;
}
void LineUtils::draw(SVG & svg,
const Lines &lines,
const char * color,

View File

@ -4,6 +4,7 @@
#include <optional>
#include <tuple>
#include <string>
#include <map>
#include <libslic3r/Line.hpp>
#include <libslic3r/SVG.hpp>
@ -92,6 +93,48 @@ public:
/// <returns>Euclid perpedicular distance</returns>
static double perp_distance(const Linef &line, Vec2d p);
/// <summary>
/// Create cross product of line direction.
/// When zero than they are parallel.
/// </summary>
/// <param name="first">First line</param>
/// <param name="second">Second line</param>
/// <returns>True when direction are same(scaled or oposit or combination) otherwise FALSE</returns>
static bool is_parallel(const Line &first, const Line &second);
/// <summary>
/// Intersection of line - no matter on line limitation
/// </summary>
/// <param name="ray1">first line</param>
/// <param name="ray2">second line</param>
/// <returns>intersection point when exist</returns>
static std::optional<Vec2d> intersection(const Line &ray1, const Line &ray2);
/// <summary>
/// Create direction from point a to point b
/// Direction vector is represented as point
/// </summary>
/// <param name="line">input line</param>
/// <returns>line direction</returns>
static Point direction(const Line &line) { return line.b - line.a; }
// line index, <a connection, b connection>
using LineConnection = std::map<size_t, std::pair<size_t, size_t>>;
/// <summary>
/// Create data structure from exPolygon lines to find if two lines are connected
/// !! not tested
/// </summary>
/// <param name="lines">Lines created from ExPolygon</param>
/// <returns>map of connected lines.</returns>
static LineConnection create_line_connection(const Lines &lines);
/// <summary>
/// Create data structure from exPolygon lines to store connected line indexes
/// </summary>
/// <param name="lines">Lines created from ExPolygon</param>
/// <returns>map of connected lines over point line::b</returns>
static std::map<size_t, size_t> create_line_connection_over_b(const Lines &lines);
static void draw(SVG & svg,
const Line &line,
const char *color = "gray",

View File

@ -2,3 +2,27 @@
using namespace Slic3r::sla;
bool PointUtils::is_equal(const Point &p1, const Point &p2) {
return p1.x() == p2.x() && p1.y() == p2.y();
}
bool PointUtils::is_majorit_x(const Point &point)
{
return abs(point.x()) > abs(point.y());
}
bool PointUtils::is_majorit_x(const Vec2d &point)
{
return fabs(point.x()) > fabs(point.y());
}
Slic3r::Point PointUtils::perp(const Point &vector)
{
return Point(-vector.x(), vector.y());
}
bool PointUtils::is_same_direction(const Point &dir1, const Point &dir2)
{
return (is_majorit_x(dir1)) ? (dir1.x() > 0) == (dir2.x() > 0) :
(dir1.y() > 0) == (dir2.y() > 0);
}

View File

@ -14,6 +14,39 @@ class PointUtils
{
public:
PointUtils() = delete;
/// <summary>
/// Is equal p1 == p2
/// </summary>
/// <param name="p1">first point</param>
/// <param name="p2">second point</param>
/// <returns>True when equal otherwise FALSE</returns>
static bool is_equal(const Point &p1, const Point &p2);
/// <summary>
/// check if absolut value of x is grater than absolut value of y
/// </summary>
/// <param name="point">input direction</param>
/// <returns>True when mayorit vaule is X other wise FALSE(mayorit value is y)</returns>
static bool is_majorit_x(const Point &point);
static bool is_majorit_x(const Vec2d &point);
/// <summary>
/// Create perpendicular vector
/// </summary>
/// <param name="vector">input vector from zero to point coordinate</param>
/// <returns>Perpendicular vector</returns>
static Point perp(const Point &vector);
/// <summary>
/// Check if both direction are on same half of the circle
/// alpha = atan2 of direction1
/// beta = atan2 of direction2
/// beta is in range from(alpha - Pi/2) to (alpha + Pi/2)
/// </summary>
/// <param name="dir1">first direction</param>
/// <param name="dir2">second direction</param>
/// <returns>True when on same half otherwise false</returns>
static bool is_same_direction(const Point &dir1, const Point &dir2);
};
} // namespace Slic3r::sla

View File

@ -1,6 +1,8 @@
#ifndef slic3r_SLA_SuppotstIslands_SampleConfig_hpp_
#define slic3r_SLA_SuppotstIslands_SampleConfig_hpp_
#include <libslic3r/libslic3r.h>
namespace Slic3r::sla {
/// <summary>
/// Configuration DTO
@ -11,37 +13,45 @@ struct SampleConfig
{
// Every point on island has at least one support point in maximum distance
// MUST be bigger than zero
double max_distance = 1.;
coord_t max_distance = 2;
coord_t half_distance = 1; // has to be half od max_distance
// Support point head radius
// MUST be bigger than zero
double head_radius = 1;
coord_t head_radius = 1; // [nano meter]
// 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.;
coord_t minimal_distance_from_outline = 0; // [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;
// 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.;
coord_t max_length_for_one_support_point = 1.;
// Maximal length of island supported by 2 points
double max_length_for_two_support_points = 1.;
coord_t 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.;
// Must be greater or equal to min_width_for_outline_support
coord_t max_width_for_center_support_line = 1.;
// Minimal width to be supported by outline
// 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
// Maximal count of align iteration
size_t count_iteration = 100;
size_t count_iteration = 100;
// Maximal distance over Voronoi diagram edges to find closest point during aligning Support point
double max_align_distance = 0;
coord_t max_align_distance = 0.;
};
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_

View File

@ -18,20 +18,23 @@ public:
{
// TODO: find valid params !!!!
SampleConfig result;
result.max_distance = 100. * config.head_diameter;
result.max_distance = 100 * config.head_diameter;
result.half_distance = result.max_distance / 2;
result.head_radius = config.head_diameter / 2;
result.minimal_distance_from_outline = config.head_diameter / 2.;
result.minimal_distance_from_outline = config.head_diameter / 2.;
result.minimal_support_distance = result.minimal_distance_from_outline +
result.half_distance;
result.max_length_for_one_support_point =
2 * result.minimal_distance_from_outline +
config.head_diameter;
double max_length_for_one_support_point =
coord_t 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 =
coord_t 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)
@ -40,7 +43,7 @@ public:
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 =
coord_t max_length_for_two_support_points =
2 * result.max_distance +
2 * config.head_diameter +
2 * result.minimal_distance_from_outline;
@ -48,23 +51,17 @@ public:
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 =
result.max_width_for_center_support_line = 2 * config.head_diameter;
coord_t min_width_for_center_support_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;
if (result.max_width_for_center_support_line < min_width_for_center_support_line)
result.max_width_for_center_support_line = min_width_for_center_support_line;
coord_t max_width_for_center_support_line = 2 * result.max_distance + config.head_diameter;
if (result.max_width_for_center_support_line > max_width_for_center_support_line)
result.max_width_for_center_support_line = max_width_for_center_support_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);
result.min_width_for_outline_support = result.max_width_for_center_support_line - 2 * config.head_diameter;
assert(result.min_width_for_outline_support >= result.max_width_for_center_support_line);
// Align support points
// TODO: propagate print resolution

View File

@ -1,12 +1,15 @@
#include "SampleIslandUtils.hpp"
#include <cmath>
#include <optional>
#include <libslic3r/VoronoiOffset.hpp>
#include "IStackFunction.hpp"
#include "EvaluateNeighbor.hpp"
#include "ParabolaUtils.hpp"
#include "VoronoiGraphUtils.hpp"
#include "VectorUtils.hpp"
#include "LineUtils.hpp"
#include "PointUtils.hpp"
#include <magic_enum/magic_enum.hpp>
#include <libslic3r/VoronoiVisualUtils.hpp>
@ -596,7 +599,9 @@ SupportIslandPoints SampleIslandUtils::sample_center_circle(
}
SupportIslandPoints SampleIslandUtils::sample_expath(
const VoronoiGraph::ExPath &path, const SampleConfig &config)
const VoronoiGraph::ExPath &path,
const Lines & lines,
const SampleConfig & config)
{
// 1) One support point
if (path.length < config.max_length_for_one_support_point) {
@ -608,7 +613,7 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
}
double max_width = VoronoiGraphUtils::get_max_width(path);
if (max_width < config.max_width_for_center_supportr_line) {
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,
@ -625,24 +630,114 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
return std::move(samples);
}
// 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
// IMPROVE: Erase continous sampling: Extract path and than sample uniformly whole path
CenterStarts center_starts;
const VoronoiGraph::Node *start_node = path.nodes.front();
// CHECK> Front of path is outline node
assert(start_node->neighbors.size() == 1);
const VoronoiGraph::Node::Neighbor *neighbor = &start_node->neighbors.front();
center_starts.emplace(neighbor, config.minimal_distance_from_outline);
SupportIslandPoints points;
points.emplace_back(create_point_on_path(path.nodes, config.minimal_distance_from_outline));
std::set<const VoronoiGraph::Node *> done; // already done nodes
done.insert(start_node);
SupportIslandPoints points; // result
while (!center_starts.empty()) {
FieldStart field_start;
std::vector<CenterStart> new_starts = sample(center_starts.front(),
config, done, points,
field_start);
center_starts.pop();
for (const CenterStart &start : new_starts) center_starts.push(start);
if (field_start.neighbor != nullptr) {
// TODO: create field
auto field = create_field(field_start, center_starts, lines, config);
}
}
// Fix first point type
if (!points.empty()) {
auto &front = points.front();
if (front->type == SupportIslandPoint::Type::center_line)
front->type = SupportIslandPoint::Type::center_line_start;
}
// TODO: remove next line, only for debug
points.push_back(create_point_on_path(path.nodes, config.minimal_distance_from_outline));
return std::move(points);
}
std::vector<SampleIslandUtils::CenterStart> SampleIslandUtils::sample(
const CenterStart & start,
const SampleConfig & config,
std::set<const VoronoiGraph::Node *> &done,
SupportIslandPoints & results,
FieldStart & field_start)
{
const VoronoiGraph::Node::Neighbor *neighbor = start.neighbor;
const VoronoiGraph::Node *node = neighbor->node;
if (done.find(node) != done.end()) return {};
done.insert(node);
VoronoiGraph::Nodes path = start.path;
std::vector<CenterStart> new_starts;
double support_in = start.support_in;
while (neighbor->max_width <= config.max_width_for_center_support_line) {
double edge_length = neighbor->edge_length;
while (edge_length > support_in) {
double ratio = support_in / edge_length;
results.push_back(
create_point(neighbor, ratio,
SupportIslandPoint::Type::center_line));
support_in += config.max_distance;
}
if (support_in > edge_length)
support_in -= edge_length;
const VoronoiGraph::Node *node = neighbor->node;
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;
if (next_neighbor == nullptr) {
next_neighbor = &node_neighbor;
continue;
}
double 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());
results.push_back(create_point_on_path(
path_reverse, config.minimal_distance_from_outline,
SupportCenterIslandPoint::Type::center_line_end3));
}
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();
} else {
neighbor = next_neighbor;
}
}
field_start.neighbor = neighbor;
field_start.last_support = config.max_distance - support_in;
return new_starts;
}
SupportIslandPoints SampleIslandUtils::sample_voronoi_graph(
const VoronoiGraph & graph,
const Lines & lines,
const SampleConfig & config,
VoronoiGraph::ExPath &longest_path)
{
@ -652,7 +747,147 @@ SupportIslandPoints SampleIslandUtils::sample_voronoi_graph(
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);
return sample_expath(longest_path, lines, config);
}
SampleIslandUtils::Field SampleIslandUtils::create_field(
const FieldStart &field_start,
CenterStarts & tiny_starts,
const Lines & lines,
const SampleConfig &config)
{
using VD = Slic3r::Geometry::VoronoiDiagram;
// DTO represents one island change from tiny part to wide part
struct WideTinyChange{
// new coordinate for line.b point
Point new_b;
// new coordinate for next line.a point
Point next_new_a;
// index to lines
size_t next_line_index;
WideTinyChange(Point new_b, Point next_new_a, size_t next_line_index)
: new_b(new_b)
, next_new_a(next_new_a)
, next_line_index(next_line_index)
{}
};
// store shortening of outline segments
// line index, <next line index + 2x shortening points>
std::map<size_t, WideTinyChange> wide_tiny_changes;
const coord_t min_width = config.min_width_for_outline_support;
// cut lines at place where neighbor has width = min_width_for_outline_support
// neighbor must be in direction from wide part to tiny part of island
auto add_wide_tiny_change = [&](const VoronoiGraph::Node::Neighbor *neighbor){
VoronoiGraph::Position position =
VoronoiGraphUtils::get_position_with_distance(neighbor, min_width, lines);
const VD::edge_type *edge = neighbor->edge;
size_t i1 = edge->cell()->source_index();
size_t i2 = edge->twin()->cell()->source_index();
const Line &l1 = lines[i1];
const Line &l2 = lines[i2];
Point p1, p2;
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, l1, l2);
if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) {
// line1 is shorten on side line1.a --> line2 is shorten on side line2.b
wide_tiny_changes.insert({i2, WideTinyChange(p2, p1, i1)});
} else {
// line1 is shorten on side line1.b
wide_tiny_changes.insert({i1, WideTinyChange(p1, p2, i2)});
}
};
const VoronoiGraph::Node::Neighbor *neighbor = field_start.neighbor;
const VoronoiGraph::Node::Neighbor *twin_neighbor = VoronoiGraphUtils::get_twin(neighbor);
add_wide_tiny_change(twin_neighbor);
std::set<const VoronoiGraph::Node*> done;
done.insert(twin_neighbor->node);
std::queue<const VoronoiGraph::Node *> process;
process.push(neighbor->node);
// all lines belongs to polygon
std::set<size_t> field_line_indexes;
while (!process.empty()) {
const VoronoiGraph::Node *node = process.front();
process.pop();
if (done.find(node) != done.end()) continue;
do {
done.insert(node);
const auto &neighbors = node->neighbors;
node = nullptr;
for (const VoronoiGraph::Node::Neighbor &neighbor : neighbors) {
if (done.find(neighbor.node) != done.end()) continue;
const VD::edge_type *edge = neighbor.edge;
size_t index1 = edge->cell()->source_index();
size_t index2 = edge->twin()->cell()->source_index();
field_line_indexes.insert(index1);
field_line_indexes.insert(index2);
if (neighbor.max_width <
config.min_width_for_outline_support) {
add_wide_tiny_change(&neighbor);
continue;
}
if (node == nullptr) {
node = neighbor.node;
} else {
process.push(neighbor.node);
}
}
} while (node != nullptr);
}
std::map<size_t, size_t> b_connection =
LineUtils::create_line_connection_over_b(lines);
// Continue along line indexes create polygon field
Points points;
points.reserve(field_line_indexes.size());
std::vector<size_t> outline_indexes;
outline_indexes.reserve(field_line_indexes.size());
auto inser_point_b = [&](size_t& index, Points& points, std::set<size_t>& done)
{
const Line &line = lines[index];
points.push_back(line.b);
const auto &connection_item = b_connection.find(index);
assert(connection_item != b_connection.end());
done.insert(index);
index = connection_item->second;
};
size_t input_index = neighbor->edge->cell()->source_index();
size_t outline_index = input_index;
std::set<size_t> done_indexes;
do {
const auto &change_item = wide_tiny_changes.find(outline_index);
if (change_item != wide_tiny_changes.end()) {
const WideTinyChange &change = change_item->second;
points.push_back(change.new_b);
points.push_back(change.next_new_a);
done_indexes.insert(outline_index);
outline_index = change.next_line_index;
}
inser_point_b(outline_index, points, done_indexes);
} while (outline_index != input_index);
Field field;
field.border.contour = Polygon(points);
// finding holes
if (done_indexes.size() < field_line_indexes.size()) {
for (const size_t &index : field_line_indexes) {
if(done_indexes.find(index) != done_indexes.end()) continue;
// new hole
Points hole_points;
size_t hole_index = index;
do {
inser_point_b(hole_index, hole_points, done_indexes);
} while (hole_index != index);
field.border.holes.emplace_back(hole_points);
}
}
return field;
}
void SampleIslandUtils::draw(SVG & svg,

View File

@ -1,6 +1,8 @@
#ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
#define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
#include <vector>
#include <libslic3r/Geometry.hpp>
#include <libslic3r/Point.hpp>
#include <libslic3r/SVG.hpp>
@ -109,23 +111,25 @@ public:
/// Decide how to sample path
/// </summary>
/// <param name="path">Path inside voronoi diagram with side branches and circles</param>
/// <param name="lines">Source lines for VG --> outline of island.</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
);
static SupportIslandPoints sample_expath(const VoronoiGraph::ExPath &path,
const Lines & lines,
const SampleConfig &config);
/// <summary>
/// Sample voronoi skeleton
/// </summary>
/// <param name="graph">Inside skeleton of island</param>
/// <param name="lines">Source lines for VG --> outline 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 Lines & lines,
const SampleConfig & config,
VoronoiGraph::ExPath &longest_path);
@ -174,6 +178,94 @@ public:
double size,
const char *color = "lightgreen",
bool write_type = true);
/// <summary>
/// DTO hold information for start sampling VD in center
/// </summary>
struct CenterStart
{
// Start of ceneter sampled line segment
const VoronoiGraph::Node::Neighbor *neighbor;
// distance to nearest support point
coord_t support_in; // [nano meters]
VoronoiGraph::Nodes path; // to sample in distance from border
CenterStart(const VoronoiGraph::Node::Neighbor *neighbor,
coord_t support_in,
VoronoiGraph::Nodes path = {})
: neighbor(neighbor), support_in(support_in), path(path)
{}
};
using CenterStarts = std::queue<CenterStart>;
/// <summary>
/// DTO extend VG neighbor with information about last sample
/// </summary>
struct FieldStart
{
// define edge where start field
const VoronoiGraph::Node::Neighbor *neighbor = nullptr;
// distance to last support
// when last support lay on neighbor edge it is negative value
coord_t last_support = 0; // [nano meters]
FieldStart() = default;
FieldStart(const VoronoiGraph::Node::Neighbor *neighbor,
coord_t last_support)
: neighbor(neighbor), last_support(last_support)
{}
};
/// <summary>
/// Sample VG in center.
/// Detection of wide neighbor which start field
/// Creating of new starts (from 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="results">Result of sampling</param>
/// <param name="field_start">Output: Wide neighbor, start of field</param>
/// <returns>New start of sampling</returns>
static std::vector<CenterStart> sample(
const CenterStart & start,
const SampleConfig & config,
std::set<const VoronoiGraph::Node *> &done,
SupportIslandPoints & results,
FieldStart& field_start);
/// <summary>
/// DTO represents area to sample
/// extend polygon with information about source lines
/// </summary>
struct Field
{
// border of field created by source lines and closing of tiny island
ExPolygon border;
// same size as polygon.points.size()
// indexes to source island lines
// in case (index == -1) it means fill the gap from tiny part of island
std::vector<size_t> source_indexes;
};
/// <summary>
/// Create field from input neighbor
/// </summary>
/// <param name="field_start">Start neighbor, first occur of wide neighbor.</param>
/// <param name="tiny_starts">Append new founded tiny parts of island.</param>
/// <param name="lines">Source lines for VG --> outline of island.</param>
/// <param name="config">Parameters for sampling.</param>
/// <returns>New created field</returns>
static Field create_field(const FieldStart & field_start,
CenterStarts &tiny_starts,
const Lines & lines,
const SampleConfig &config);
};
} // namespace Slic3r::sla

View File

@ -19,6 +19,8 @@ struct SupportIslandPoint
center_line,
center_line_end, // end of branch
center_line_end2, // start of main path(only one per VD)
center_line_end3, // end in continous sampling
center_line_start, // first sample
center_circle,
center_circle_end, // circle finish by one point (one end per circle -
// need allign)
@ -51,7 +53,9 @@ struct SupportIslandPoint
Type::one_center_point,
Type::two_points,
Type::center_line_end,
Type::center_line_end2});
Type::center_line_end2,
Type::center_line_end3,
Type::center_line_start});
return cant_move.find(type) == cant_move.end();
//*/
}

View File

@ -41,6 +41,18 @@ Slic3r::Vec2d VoronoiGraphUtils::to_point_d(const VD::vertex_type *vertex)
return Vec2d(vertex->x(), vertex->y());
}
Slic3r::Point VoronoiGraphUtils::to_direction(const VD::edge_type *edge)
{
return to_direction_d(edge).cast<coord_t>();
}
Slic3r::Vec2d VoronoiGraphUtils::to_direction_d(const VD::edge_type *edge)
{
const VD::vertex_type *v0 = edge->vertex0();
const VD::vertex_type *v1 = edge->vertex1();
return Vec2d(v1->x() - v0->x(), v1->y() - v0->y());
}
bool VoronoiGraphUtils::is_coord_in_limits(const VD::coordinate_type &coord,
const coord_t & source,
double max_distance)
@ -768,16 +780,28 @@ VoronoiGraph::ExPath VoronoiGraphUtils::create_longest_path(
return longest_path;
}
const VoronoiGraph::Node *VoronoiGraphUtils::get_twin_node(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 n : neighbor->node->neighbors) {
if (n.edge == twin_edge) return n.node;
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)
{
return get_twin(neighbor)->node;
}
bool VoronoiGraphUtils::is_opposit_direction(const VD::edge_type *edge, const Line &line)
{
Point dir_line = LineUtils::direction(line);
Point dir_edge = VoronoiGraphUtils::to_direction(edge);
return !PointUtils::is_same_direction(dir_line, dir_edge);
}
Slic3r::Point VoronoiGraphUtils::create_edge_point(
const VoronoiGraph::Position &position)
{
@ -810,6 +834,84 @@ 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(
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
return result;
}
assert(edge->is_finite());
Slic3r::Line edge_line(to_point(edge->vertex0()), to_point(edge->vertex1()));
const Slic3r::Line &source_line = lines[edge->cell()->source_index()];
if (LineUtils::is_parallel(edge_line, source_line)) {
// Every point on parallel lines has same distance
return result;
}
double half_width = width / 2.;
double a_dist = source_line.perp_distance_to(edge_line.a);
double b_dist = source_line.perp_distance_to(edge_line.b);
// check if half_width is in range from a_dist to b_dist
if (a_dist > b_dist) {
if (b_dist >= half_width) {
// vertex1 is closer to width
result.ratio = 1.;
return result;
} else if (a_dist <= half_width) {
// vertex0 is closer to width
return result;
}
} else {
// a_dist < b_dist
if (a_dist >= half_width) {
// vertex0 is closer to width
return result;
} else if (b_dist <= half_width) {
// vertex1 is closer to width
result.ratio = 1.;
return result;
}
}
result.ratio = fabs((a_dist - half_width) / (a_dist - b_dist));
return result;
}
Slic3r::Point VoronoiGraphUtils::point_on_line(
const VoronoiGraph::Position &position, const Line &line)
{
const VD::edge_type* edge = position.neighbor->edge;
assert(edge->is_linear());
Point edge_point = create_edge_point(position);
Point dir = to_point(edge->vertex0()) - to_point(edge->vertex1());
Line edge_line(edge_point, edge_point + PointUtils::perp(dir));
std::optional<Vec2d> intersection = LineUtils::intersection(line, edge_line);
assert(intersection.has_value());
return intersection->cast<coord_t>();
}
std::pair<Slic3r::Point, Slic3r::Point> VoronoiGraphUtils::point_on_lines(
const VoronoiGraph::Position &position,
const Line & first,
const Line & second)
{
const VD::edge_type *edge = position.neighbor->edge;
assert(edge->is_linear());
Point edge_point = create_edge_point(position);
Point dir = to_point(edge->vertex0()) - to_point(edge->vertex1());
Line edge_line(edge_point, edge_point + PointUtils::perp(dir));
std::optional<Vec2d> first_intersection = LineUtils::intersection(first, edge_line);
assert(first_intersection.has_value());
std::optional<Vec2d> second_intersection = LineUtils::intersection(second, edge_line);
assert(second_intersection.has_value());
return {first_intersection->cast<coord_t>(),
second_intersection->cast<coord_t>()};
}
VoronoiGraph::Position VoronoiGraphUtils::align(
const VoronoiGraph::Position &position, const Point &to, double max_distance)
{

View File

@ -38,14 +38,28 @@ public:
/// </summary>
/// <param name="vertex">Input point pointer(double precission)</param>
/// <returns>Convertedf point(int preccission)</returns>
static Slic3r::Point to_point(const VD::vertex_type *vertex);
static Point to_point(const VD::vertex_type *vertex);
/// <summary>
/// Convert point type between voronoi and slicer format
/// </summary>
/// <param name="vertex">Input vertex</param>
/// <returns>created vector</returns>
static Slic3r::Vec2d to_point_d(const VD::vertex_type* vertex);
static Vec2d to_point_d(const VD::vertex_type* vertex);
/// <summary>
/// create direction from Voronoi edge
/// </summary>
/// <param name="edge">input</param>
/// <returns>direction --> (vertex1 - vertex0)</returns>
static Point to_direction(const VD::edge_type *edge);
/// <summary>
/// create direction from Voronoi edge
/// </summary>
/// <param name="edge">input</param>
/// <returns>direction --> (vertex1 - vertex0)</returns>
static Vec2d to_direction_d(const VD::edge_type *edge);
/// <summary>
/// check if coord is in limits for coord_t
@ -70,13 +84,13 @@ public:
double max_distance);
/// <summary>
/// Private function to help convert edge without vertex to line
/// PRIVATE: function to help convert edge without vertex to line
/// </summary>
/// <param name="point1">VD source point</param>
/// <param name="point2">VD source point</param>
/// <param name="maximal_distance">Maximal distance from source point</param>
/// <returns>Line segment between lines</returns>
static Slic3r::Line create_line_between_source_points(
static Line create_line_between_source_points(
const Point &point1, const Point &point2, double maximal_distance);
/// <summary>
@ -89,9 +103,9 @@ public:
/// <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>
static std::optional<Slic3r::Line> to_line(const VD::edge_type &edge,
const Points & points,
double maximal_distance);
static std::optional<Line> to_line(const VD::edge_type &edge,
const Points & points,
double maximal_distance);
/// <summary>
/// close polygon defined by lines
/// close points will convert to their center
@ -103,11 +117,11 @@ public:
/// <param name="minimal_distance">Merge points closer than minimal_distance</param>
/// <param name="count_points">Count checking points, create help points for result polygon</param>
/// <returns>Valid CCW polygon with center inside of polygon</returns>
static Slic3r::Polygon to_polygon(const Lines &lines,
const Point &center,
double maximal_distance,
double minimal_distance,
size_t count_points);
static Polygon to_polygon(const Lines &lines,
const Point &center,
double maximal_distance,
double minimal_distance,
size_t count_points);
/// <summary>
/// Convert cell to polygon
/// Source for VD must be only point to create VD with only line segments
@ -117,9 +131,9 @@ public:
/// <param name="points">source points for VD</param>
/// <param name="maximal_distance">maximal distance from source point - only for infinite edges(cells)</param>
/// <returns>polygon created by cell</returns>
static Slic3r::Polygon to_polygon(const VD::cell_type & cell,
const Slic3r::Points &points,
double maximal_distance);
static Polygon to_polygon(const VD::cell_type &cell,
const Points & points,
double maximal_distance);
// return node from graph by vertex, when no exists create one
static VoronoiGraph::Node *getNode(VoronoiGraph & graph,
@ -148,18 +162,37 @@ public:
/// <returns>Point from source points.</returns>
static const Point& retrieve_point(const Points &points, const VD::cell_type &cell);
static Slic3r::Point get_parabola_point(const VD::edge_type &parabola, const Slic3r::Lines &lines);
static Slic3r::Line get_parabola_line(const VD::edge_type &parabola, const Lines &lines);
/// <summary>
/// PRIVATE: function to get parabola focus point
/// </summary>
/// <param name="parabola">curved edge</param>
/// <param name="lines">source lines</param>
/// <returns>Parabola focus point</returns>
static Point get_parabola_point(const VD::edge_type &parabola, const Lines &lines);
/// <summary>
/// PRIVATE:
/// </summary>
/// <param name="parabola"></param>
/// <param name="lines"></param>
/// <returns></returns>
static Line get_parabola_line(const VD::edge_type &parabola, const Lines &lines);
/// <summary>
/// Construct parabola from curved edge
/// </summary>
/// <param name="edge">curved edge</param>
/// <param name="lines">Voronoi diagram source lines</param>
/// <returns>Parabola represented shape of edge</returns>
static Parabola get_parabola(const VD::edge_type &edge, const Lines &lines);
/// <summary>
/// Calculate length
/// Calculate length of curved edge
/// </summary>
/// <param name="edge">curved edge</param>
/// <param name="lines">Voronoi diagram source lines</param>
/// <returns>edge length</returns>
static double calculate_length_of_parabola(
const VD::edge_type & edge,
const Lines & lines);
static double calculate_length_of_parabola(const VD::edge_type &edge, const Lines &lines);
/// <summary>
/// Calculate length of edge line segment or curve - parabola.
@ -167,8 +200,7 @@ public:
/// <param name="edge">Input edge to calcuate length</param>
/// <param name="lines">Source for Voronoi diagram. It contains parabola parameters</param>
/// <returns>The length of edge</returns>
static double calculate_length(const VD::edge_type &edge,
const Lines & lines);
static double calculate_length(const VD::edge_type &edge, const Lines &lines);
/// <summary>
/// Calculate maximal distance to outline and multiply by two(must be similar on both side)
@ -176,8 +208,7 @@ public:
/// <param name="edge">Input edge.</param>
/// <param name="lines">Source for Voronoi diagram. It contains parabola parameters</param>
/// <returns>Maximal island width along edge</returns>
static double calculate_max_width(const VD::edge_type &edge,
const Lines & lines);
static double calculate_max_width(const VD::edge_type &edge, const Lines &lines);
/// <summary>
/// calculate distances to border of island and length on skeleton
@ -285,13 +316,27 @@ public:
static VoronoiGraph::ExPath create_longest_path(
const VoronoiGraph::Node *start_node);
/// <summary>
/// Find twin neighbor
/// </summary>
/// <param name="neighbor">neighbor</param>
/// <returns>Twin neighbor</returns>
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 *VoronoiGraphUtils::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
/// </summary>
/// <param name="edge">edge has direction from vertex0 to vertex1</param>
/// <param name="line">line has direction from point a to point b</param>
/// <returns>True when oposit direction otherwise FALSE</returns>
static bool is_opposit_direction(const VD::edge_type *edge, const Line &line);
/// <summary>
/// Create point on edge defined by neighbor
@ -302,6 +347,39 @@ public:
static Point create_edge_point(const VoronoiGraph::Position& position);
static Point create_edge_point(const VD::edge_type *edge, double ratio);
/// <summary>
/// Find position on VD edge with width
/// </summary>
/// <param name="neighbor">Edge for searching position</param>
/// <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(
const VoronoiGraph::Node::Neighbor *neighbor,
coord_t width,
const Lines & lines);
/// <summary>
/// Calculate point on line correspond to edge position
/// </summary>
/// <param name="position">Position on edge</param>
/// <param name="line">Line must be source of edge</param>
/// <returns>Point lay on line defined by position on edge</returns>
static Point point_on_line(const VoronoiGraph::Position& position, const Line& line);
/// <summary>
/// calculate both point on source lines correspond to edge postion
/// Faster way to get both point_on_line
/// </summary>
/// <param name="position">Position on edge</param>
/// <param name="line">Line must be source of edge</param>
/// <param name="line">Line must be source of edge</param>
/// <returns>pair of point lay on lines cirrespond to position</returns>
static std::pair<Point, Point> point_on_lines(
const VoronoiGraph::Position &position,
const Line & first,
const Line & second);
/// <summary>
/// align "position" close to point "to"
/// </summary>

View File

@ -373,13 +373,15 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island,
SampleConfig create_sample_config(double size) {
SampleConfig cfg;
cfg.max_distance = 3 * size + 0.1;
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_support_distance = cfg.minimal_distance_from_outline + cfg.half_distance;
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;
cfg.max_width_for_center_support_line = size;
cfg.min_width_for_outline_support = cfg.max_width_for_center_support_line;
cfg.minimal_move = std::max(1000., size/1000);
cfg.count_iteration = 100;
@ -408,7 +410,7 @@ TEST_CASE("Sampling speed test on FrogLegs", "[VoronoiSkeleton]")
for (int i = 0; i < 100; ++i) {
VoronoiGraph::ExPath longest_path;
VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines);
auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path);
auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, lines, cfg, longest_path);
}
}
@ -430,7 +432,7 @@ TEST_CASE("Speed align", "[VoronoiSkeleton]")
VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines);
for (int i = 0; i < 100; ++i) {
auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, cfg, longest_path);
auto samples = SampleIslandUtils::sample_voronoi_graph(skeleton, lines, cfg, longest_path);
SampleIslandUtils::align_samples(samples, island, cfg);
}
}