add minimal width to edge

Fix creation of Field and sampling outline
This commit is contained in:
Filip Sykala 2021-04-14 11:50:36 +02:00 committed by Lukas Matena
parent 0033deb1d4
commit f44b0d51f1
9 changed files with 356 additions and 160 deletions

View File

@ -28,7 +28,7 @@ void ExpandNeighbor::process(CallStack &call_stack)
const VoronoiGraph::Node &next_node = *neighbor.node;
// is next node leaf ?
if (next_node.neighbors.size() == 1) {
VoronoiGraph::Path side_branch({&next_node}, neighbor.edge_length);
VoronoiGraph::Path side_branch({&next_node}, neighbor.length());
data.side_branches.push(std::move(side_branch));
return;
}
@ -39,6 +39,6 @@ void ExpandNeighbor::process(CallStack &call_stack)
call_stack.emplace(std::move(post_process_neighbor));
call_stack.emplace(
std::make_unique<EvaluateNeighbor>(neighbor_path, neighbor.node,
neighbor.edge_length,
neighbor.length(),
data.act_path));
}

View File

@ -72,6 +72,30 @@ bool ParabolaUtils::is_over_zero(const ParabolaSegment &parabola)
return is_positive_x1 != is_positive_x2;
}
void ParabolaUtils::draw(SVG & svg,
const ParabolaSegment &parabola,
const char * color,
coord_t width,
double discretization_step)
{
using VD = Slic3r::Geometry::VoronoiDiagram;
std::vector<Voronoi::Internal::point_type> parabola_samples(
{parabola.from, parabola.to});
VD::point_type source_point = parabola.focus;
VD::segment_type source_segment(parabola.directrix.a,
parabola.directrix.b);
::boost::polygon::voronoi_visual_utils<double>::discretize(
source_point, source_segment, discretization_step, &parabola_samples);
for (size_t index = 1; index < parabola_samples.size(); ++index) {
const auto& s0 = parabola_samples[index - 1];
const auto& s1 = parabola_samples[index];
Line l(Point(s0.x(), s0.y()), Point(s1.x(), s1.y()));
svg.draw(l, color, width);
}
}
// PRIVATE
double ParabolaUtils::parabola_arc_length(double x)
{

View File

@ -2,6 +2,7 @@
#define slic3r_SLA_SuppotstIslands_ParabolaUtils_hpp_
#include "Parabola.hpp"
#include <libslic3r/SVG.hpp>
namespace Slic3r::sla {
@ -44,6 +45,20 @@ public:
/// <returns>True when interval contain top of parabola otherwise False</returns>
static bool is_over_zero(const ParabolaSegment &parabola);
/// <summary>
/// Connvert parabola to svg by sampling
/// </summary>
/// <param name="svg">outputfile</param>
/// <param name="parabola">parabola to draw</param>
/// <param name="color">color</param>
/// <param name="width">width</param>
/// <param name="discretization_step">step between discretized lines</param>
static void draw(SVG & svg,
const ParabolaSegment &parabola,
const char * color,
coord_t width,
double discretization_step = 1e3);
private:
/// <summary>
/// Integral of parabola: y = x^2 from zero to point x

View File

@ -17,7 +17,7 @@
#include <libslic3r/ClipperUtils.hpp> // allign
// comment definition of NDEBUG to enable assert()
// #define NDEBUG
//#define NDEBUG
#define SLA_SAMPLING_STORE_FIELD_TO_SVG
#define SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG
@ -50,11 +50,11 @@ std::unique_ptr<SupportIslandPoint> SampleIslandUtils::create_point_on_path(
}
const VoronoiGraph::Node::Neighbor *neighbor =
VoronoiGraphUtils::get_neighbor(prev_node, node);
actual_distance += neighbor->edge_length;
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->edge_length;
double over_ratio = previous_distance / neighbor->length();
double ratio = 1. - over_ratio;
return create_point(neighbor, ratio, type);
}
@ -126,14 +126,14 @@ SupportIslandPoints SampleIslandUtils::sample_side_branch(
std::move_iterator(side_samples.end()));
}
}
while (distance < neighbor->edge_length) {
double edge_ratio = distance / neighbor->edge_length;
while (distance < neighbor->length()) {
double edge_ratio = distance / neighbor->length();
result.push_back(
create_point(neighbor, edge_ratio, SupportIslandPoint::Type::center_line)
);
distance += sample_distance;
}
distance -= neighbor->edge_length;
distance -= neighbor->length();
prev_node = node;
}
assert(fabs(distance - (sample_distance - cfg.side_distance)) < 1e-5);
@ -310,12 +310,12 @@ void SampleIslandUtils::sample_center_circle_end(
const CenterLineConfiguration & cfg,
SupportIslandPoints & result)
{
double distance = neighbor_distance + node_distance + neighbor.edge_length;
double distance = neighbor_distance + node_distance + neighbor.length();
if (distance < cfg.max_sample_distance) { // no need add support point
if (neighbor_distance > node_distance + neighbor.edge_length)
neighbor_distance = node_distance + neighbor.edge_length;
if (node_distance > neighbor_distance + neighbor.edge_length)
node_distance = neighbor_distance + neighbor.edge_length;
if (neighbor_distance > node_distance + neighbor.length())
neighbor_distance = node_distance + neighbor.length();
if (node_distance > neighbor_distance + neighbor.length())
node_distance = neighbor_distance + neighbor.length();
return;
}
size_t count_supports = static_cast<size_t>(
@ -328,11 +328,11 @@ void SampleIslandUtils::sample_center_circle_end(
neighbor_distance = 0.;
count_supports -= 1;
if (count_supports == 0) {
if (node_distance > neighbor.edge_length)
node_distance = neighbor.edge_length;
if (node_distance > neighbor.length())
node_distance = neighbor.length();
return;
}
distance = node_distance + neighbor.edge_length;
distance = node_distance + neighbor.length();
distance_between = distance / (count_supports + 1);
}
VoronoiGraph::Nodes nodes = done_nodes; // copy, could be more neighbor
@ -341,7 +341,7 @@ void SampleIslandUtils::sample_center_circle_end(
double distance_from_neighbor = i * (distance_between) - neighbor_distance;
result.push_back(
create_point_on_path(nodes, distance_from_neighbor, SupportIslandPoint::Type::center_circle_end2));
double distance_support_to_node = fabs(neighbor.edge_length -
double distance_support_to_node = fabs(neighbor.length() -
distance_from_neighbor);
if (node_distance > distance_support_to_node)
node_distance = distance_support_to_node;
@ -396,7 +396,7 @@ double get_distance_to_support_point(const VoronoiGraph::Node *node,
};
std::priority_queue<Item, std::vector<Item>, OrderDistanceFromNearest> process;
for (const VoronoiGraph::Node::Neighbor &neighbor : node->neighbors)
process.emplace(node, neighbor.node, neighbor.edge_length);
process.emplace(node, neighbor.node, neighbor.length());
while (!process.empty()) {
Item i = process.top();
@ -411,7 +411,7 @@ double get_distance_to_support_point(const VoronoiGraph::Node *node,
}
for (const VoronoiGraph::Node::Neighbor &neighbor :i.node->neighbors) {
if (neighbor.node == i.prev_node) continue;
double distance = i.act_distance + neighbor.edge_length;
double distance = i.act_distance + neighbor.length();
if (distance > maximal_search) continue;
process.emplace(i.node, neighbor.node, distance);
}
@ -443,8 +443,8 @@ SupportDistanceMap create_support_distance_map(const SupportIslandPoints &suppor
const VoronoiGraph::Position &position = ptr->position;
const VoronoiGraph::Node *node = position.neighbor->node;
const VoronoiGraph::Node *twin_node = VoronoiGraphUtils::get_twin_node(position.neighbor);
double distance = (1 - position.ratio) * position.neighbor->edge_length;
double twin_distance = position.ratio * position.neighbor->edge_length;
double distance = (1 - position.ratio) * position.neighbor->length();
double twin_distance = position.ratio * position.neighbor->length();
auto item = support_distance_map.find(node);
if (item == support_distance_map.end()) {
@ -584,14 +584,14 @@ SupportIslandPoints SampleIslandUtils::sample_center_circle(
NodeDistance next_nd = nd; // copy
next_nd.nodes.insert(next_nd.nodes.begin(), neighbor.node);
next_nd.distance_from_support_point += neighbor.edge_length;
next_nd.distance_from_support_point += neighbor.length();
// exist place for sample:
while (next_nd.distance_from_support_point >
cfg.max_sample_distance) {
double distance_from_node = next_nd
.distance_from_support_point -
nd.distance_from_support_point;
double ratio = distance_from_node / neighbor.edge_length;
double ratio = distance_from_node / neighbor.length();
result.push_back(
create_point(&neighbor, ratio, SupportIslandPoint::Type::center_circle));
next_nd.distance_from_support_point -= cfg.max_sample_distance;
@ -602,20 +602,28 @@ SupportIslandPoints SampleIslandUtils::sample_center_circle(
return result;
}
void SampleIslandUtils::sample_field(
VoronoiGraph::Position& field_start,
SupportIslandPoints& points,
CenterStarts& center_starts,
std::set<const VoronoiGraph::Node *>& done,
const Lines & lines,
const SampleConfig &config)
SupportIslandPoints SampleIslandUtils::sample_voronoi_graph(
const VoronoiGraph & graph,
const Lines & lines,
const SampleConfig & config,
VoronoiGraph::ExPath &longest_path)
{
auto field = create_field(field_start, center_starts, done, lines, config);
SupportIslandPoints outline_support = sample_outline(field, config);
points.insert(points.end(), std::move_iterator(outline_support.begin()),
std::move_iterator(outline_support.end()));
// TODO: sample field inside
const VoronoiGraph::Node *start_node =
VoronoiGraphUtils::getFirstContourNode(graph);
// every island has to have a point on contour
assert(start_node != nullptr);
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
// longest_path = create_longest_path_recursive(start_node);
#ifdef SLA_SAMPLING_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);
}
SupportIslandPoints SampleIslandUtils::sample_expath(
@ -653,8 +661,7 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
// TODO: 3) Triangle of points
// eval outline and find three point create almost equilateral triangle
// IMPROVE: Erase continous sampling: Extract path and than sample uniformly whole path
// IMPROVE: Erase continous sampling: Extract ExPath and than sample uniformly whole ExPath
CenterStarts center_starts;
const VoronoiGraph::Node *start_node = path.nodes.front();
// CHECK> Front of path is outline node
@ -662,26 +669,19 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
const VoronoiGraph::Node::Neighbor *neighbor = &start_node->neighbors.front();
std::set<const VoronoiGraph::Node *> done; // already done nodes
SupportIslandPoints points; // result
if (neighbor->max_width > config.max_width_for_center_support_line) {
VoronoiGraph::Position field_start = VoronoiGraphUtils::get_position_with_distance(
neighbor, config.min_width_for_outline_support, lines);
double center_sample_distance = neighbor->edge_length * field_start.ratio;
if (center_sample_distance > config.max_distance / 2.) {
// sample field from node, start without change on begining
sample_field(field_start, points, center_starts, done, lines, config);
} else {
const VoronoiGraph::Node::Neighbor *twin = VoronoiGraphUtils::get_twin(neighbor);
done.insert(neighbor->node);
coord_t support_in = neighbor->edge_length - center_sample_distance +
config.max_distance / 2;
center_starts.push(CenterStart(twin, support_in, {neighbor->node}));
sample_field(field_start, points, center_starts, done, lines, config);
}
} else {
if (neighbor->max_width() < config.max_width_for_center_support_line) {
// start sample center
done.insert(start_node);
center_starts.push(CenterStart(neighbor, config.minimal_distance_from_outline));
coord_t support_in = config.minimal_distance_from_outline;
center_starts.push(CenterStart(neighbor, support_in));
} else {
// start sample field
VoronoiGraph::Position field_start =
VoronoiGraphUtils::get_position_with_distance(
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 =
@ -693,10 +693,23 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
field_start = {};
}
}
return points;
}
void SampleIslandUtils::sample_field(VoronoiGraph::Position &field_start,
SupportIslandPoints & points,
CenterStarts & center_starts,
std::set<const VoronoiGraph::Node *> &done,
const Lines & lines,
const SampleConfig &config)
{
auto field = create_field(field_start, center_starts, done, lines, config);
SupportIslandPoints outline_support = sample_outline(field, config);
points.insert(points.end(), std::move_iterator(outline_support.begin()),
std::move_iterator(outline_support.end()));
// TODO: sample field inside
}
std::vector<SampleIslandUtils::CenterStart> SampleIslandUtils::sample_center(
const CenterStart & start,
const SampleConfig & config,
@ -707,13 +720,13 @@ std::vector<SampleIslandUtils::CenterStart> SampleIslandUtils::sample_center(
{
const VoronoiGraph::Node::Neighbor *neighbor = start.neighbor;
const VoronoiGraph::Node *node = neighbor->node;
// already sampled line
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;
do {
double edge_length = neighbor->edge_length;
double edge_length = neighbor->length();
while (edge_length >= support_in) {
double ratio = support_in / edge_length;
results.push_back(
@ -724,6 +737,7 @@ std::vector<SampleIslandUtils::CenterStart> SampleIslandUtils::sample_center(
support_in -= edge_length;
const VoronoiGraph::Node *node = neighbor->node;
path.push_back(node);
done.insert(node);
const VoronoiGraph::Node::Neighbor *next_neighbor = nullptr;
for (const auto &node_neighbor : node->neighbors) {
if (done.find(node_neighbor.node) != done.end()) continue;
@ -754,11 +768,14 @@ std::vector<SampleIslandUtils::CenterStart> SampleIslandUtils::sample_center(
} else {
neighbor = next_neighbor;
}
} while (neighbor->max_width <= config.max_width_for_center_support_line);
} while (neighbor->max_width() <= config.max_width_for_center_support_line);
// create field start
field_start = VoronoiGraphUtils::get_position_with_distance(
neighbor, config.min_width_for_outline_support, lines);
double edge_length = neighbor->edge_length;
// sample rest of neighbor before field
double edge_length = neighbor->length();
double sample_length = edge_length * field_start->ratio;
while (sample_length > support_in) {
double ratio = support_in / edge_length;
@ -769,29 +786,6 @@ std::vector<SampleIslandUtils::CenterStart> SampleIslandUtils::sample_center(
return new_starts;
}
SupportIslandPoints SampleIslandUtils::sample_voronoi_graph(
const VoronoiGraph & graph,
const Lines & lines,
const SampleConfig & config,
VoronoiGraph::ExPath &longest_path)
{
const VoronoiGraph::Node *start_node =
VoronoiGraphUtils::getFirstContourNode(graph);
// every island has to have a point on contour
assert(start_node != nullptr);
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
// longest_path = create_longest_path_recursive(start_node);
#ifdef SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG
{
static int counter=0;
SVG svg("voronoiGraph"+std::to_string(counter++)+".svg", LineUtils::create_bounding_box(lines));
LineUtils::draw(svg, lines, "black",0., true);
VoronoiGraphUtils::draw(svg, graph, 1e6, true);
}
#endif // SLA_SAMPLING_STORE_VORONOI_GRAPH_TO_SVG
return sample_expath(longest_path, lines, config);
}
SampleIslandUtils::Field SampleIslandUtils::create_field(
const VoronoiGraph::Position & field_start,
@ -844,10 +838,11 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
const VoronoiGraph::Node * source_node)->bool {
const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor;
// TODO: check not only one neighbor but all path to edge
// IMPROVE: check not only one neighbor but all path to edge
coord_t rest_size = static_cast<coord_t>(neighbor->length() * (1. - position.ratio));
if (VoronoiGraphUtils::is_last_neighbor(neighbor) &&
neighbor->edge_length * (1. - position.ratio) <= config.max_distance / 2)
return false;
rest_size <= config.max_distance / 2)
return false; // no change only rich outline
// function to add sorted change from wide to tiny
// stored uder line index or line shorten in point b
@ -864,8 +859,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
};
Point p1, p2;
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position,
lines);
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines);
const VD::edge_type *edge = neighbor->edge;
size_t i1 = edge->cell()->source_index();
size_t i2 = edge->twin()->cell()->source_index();
@ -879,7 +873,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
// line1 is shorten on side line1.b
add(p1, p2, i1, i2);
}
coord_t support_in = neighbor->edge_length * position.ratio + config.max_distance/2;
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_done.insert(source_node);
@ -893,14 +887,16 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
std::set<const VoronoiGraph::Node*> done;
done.insert(twin_neighbor->node);
std::queue<const VoronoiGraph::Node *> process;
process.push(neighbor->node);
using ProcessItem = std::pair<const VoronoiGraph::Node *, const VoronoiGraph::Node *>;
std::queue<ProcessItem> process;
process.push({twin_neighbor->node, neighbor->node});
// all lines belongs to polygon
std::set<size_t> field_line_indexes;
while (!process.empty()) {
const VoronoiGraph::Node *node = process.front();
const VoronoiGraph::Node *prev_node = nullptr;
const ProcessItem &item = process.front();
const VoronoiGraph::Node *node = item.second;
const VoronoiGraph::Node *prev_node = item.first;
process.pop();
if (done.find(node) != done.end()) continue;
do {
@ -913,7 +909,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
size_t index2 = edge->twin()->cell()->source_index();
field_line_indexes.insert(index1);
field_line_indexes.insert(index2);
if (VoronoiGraphUtils::is_last_neighbor(&neighbor) || neighbor.max_width < min_width) {
if (neighbor.min_width() < min_width) {
VoronoiGraph::Position position =
VoronoiGraphUtils::get_position_with_distance(&neighbor, min_width, lines);
if(add_wide_tiny_change(position, node))
@ -923,7 +919,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
if (next_node == nullptr) {
next_node = neighbor.node;
} else {
process.push(neighbor.node);
process.push({node, neighbor.node});
}
}
prev_node = node;
@ -996,7 +992,9 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
points.reserve(field_line_indexes.size());
std::vector<size_t> outline_indexes;
outline_indexes.reserve(field_line_indexes.size());
size_t input_index = neighbor->edge->cell()->source_index();
size_t input_index1 = neighbor->edge->cell()->source_index();
size_t input_index2 = neighbor->edge->twin()->cell()->source_index();
size_t input_index = std::min(input_index1, input_index2);
size_t outline_index = input_index;
std::set<size_t> done_indexes;
do {

View File

@ -108,17 +108,6 @@ public:
const CenterLineConfiguration & cfg,
SupportIslandPoints & result);
/// <summary>
/// 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 Lines & lines,
const SampleConfig &config);
/// <summary>
/// Sample voronoi skeleton
/// </summary>
@ -134,6 +123,18 @@ public:
const SampleConfig & config,
VoronoiGraph::ExPath &longest_path);
/// <summary>
/// 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 Lines & lines,
const SampleConfig &config);
/// <summary>
/// Transform support point to slicer points
/// </summary>
@ -250,9 +251,9 @@ public:
/// <summary>
/// Create field from input neighbor
/// </summary>
/// <param name="field_start">Start neighbor, first occur of wide neighbor.</param>
/// <param name="field_start">Start position, change from tiny to wide.</param>
/// <param name="tiny_starts">Append new founded tiny parts of island.</param>
/// <param name="tiny_done">Already sampled node sets. Filled only node inside field imediate after change</param>
/// <param name="tiny_done">Already sampled node sets.</param>
/// <param name="lines">Source lines for VG --> outline of island.</param>
/// <param name="config">Containe Minimal width in field and sample distance for center line</param>
/// <returns>New created field</returns>

View File

@ -57,23 +57,40 @@ struct VoronoiGraph::Node::Neighbor
const VD::edge_type *edge;
// pointer on graph node structure
const Node *node;
/// <summary>
/// DTO represents size property of one Neighbor
/// </summary>
struct Size{
// length edge between vertices
double length;
// length edge between vertices
double edge_length;
// widht is distance between outlines
// maximal width
coord_t min_width;
// minimal widht
coord_t max_width;
// maximal widht of sland(distance to outline)
double max_width;
Size(double length, coord_t min_width, coord_t max_width)
: length(length), min_width(min_width), max_width(max_width)
{}
};
std::shared_ptr<Size> size;
public:
Neighbor(const VD::edge_type *edge, const Node *node, double edge_length, double max_width)
Neighbor(const VD::edge_type * edge,
const Node * node,
std::shared_ptr<Size> size)
: edge(edge)
, node(node)
, edge_length(edge_length)
, max_width(max_width)
, size(std::move(size))
{}
// accessor to member
double length() const { return size->length; }
coord_t min_width() const { return size->min_width; }
coord_t max_width() const { return size->max_width; }
};
/// <summary>
/// DTO represents path over nodes of VoronoiGraph
/// store queue of Nodes

View File

@ -423,6 +423,79 @@ double VoronoiGraphUtils::calculate_max_width(
return max_width(parabola.focus);
}
std::pair<coord_t, coord_t> VoronoiGraphUtils::calculate_width(
const VD::edge_type &edge, const Lines &lines)
{
if (edge.is_linear())
return calculate_width_for_line(edge, lines);
return calculate_width_for_parabola(edge, lines);
}
std::pair<coord_t, coord_t> VoronoiGraphUtils::calculate_width_for_line(
const VD::edge_type &line_edge, const Lines &lines)
{
assert(line_edge.is_linear());
// edge line could be initialized by 2 points
if (line_edge.cell()->contains_point()) {
const Line &source_line = lines[line_edge.cell()->source_index()];
Point source_point;
if (line_edge.cell()->source_category() ==
boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT)
source_point = source_line.a;
else {
assert(line_edge.cell()->source_category() ==
boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT);
source_point = source_line.b;
}
return min_max_width(line_edge, source_point);
}
assert(line_edge.cell()->contains_segment());
assert(!line_edge.twin()->cell()->contains_point());
assert(line_edge.twin()->cell()->contains_segment());
const Line & line = lines[line_edge.cell()->source_index()];
Point v0 = to_point(line_edge.vertex0());
Point v1 = to_point(line_edge.vertex1());
double distance0 = line.perp_distance_to(v0);
double distance1 = line.perp_distance_to(v1);
std::pair<coord_t, coord_t> min_max(2 * static_cast<coord_t>(distance0),
2 * static_cast<coord_t>(distance1));
if (min_max.first > min_max.second)
std::swap(min_max.first, min_max.second);
return min_max;
}
std::pair<coord_t, coord_t> VoronoiGraphUtils::calculate_width_for_parabola(
const VD::edge_type &parabola_edge, const Lines &lines)
{
assert(parabola_edge.is_curved());
// distance to point and line on parabola is same
Parabola parabola = get_parabola(parabola_edge, lines);
Point v0 = to_point(parabola_edge.vertex0());
Point v1 = to_point(parabola_edge.vertex1());
ParabolaSegment parabola_segment(parabola, v0, v1);
std::pair<coord_t, coord_t> min_max = min_max_width(parabola_edge, parabola.focus);
if (ParabolaUtils::is_over_zero(parabola_segment)) {
min_max.first = parabola.directrix.perp_distance_to(parabola.focus);
}
return min_max;
}
std::pair<coord_t, coord_t> VoronoiGraphUtils::min_max_width(
const VD::edge_type &edge, const Point &point)
{
auto distance = [](const VD::vertex_type *vertex,
const Point & point) -> coord_t {
Vec2d point_d = point.cast<double>();
Vec2d diff = point_d - to_point_d(vertex);
double distance = diff.norm();
return static_cast<coord_t>(std::round(distance));
};
std::pair<coord_t, coord_t> result(2 * distance(edge.vertex0(), point),
2 * distance(edge.vertex1(), point));
if (result.first > result.second) std::swap(result.first, result.second);
return result;
};
VoronoiGraph VoronoiGraphUtils::create_skeleton(const VD &vd, const Lines &lines)
{
// vd should be annotated.
@ -458,16 +531,16 @@ VoronoiGraph VoronoiGraphUtils::create_skeleton(const VD &vd, const Lines &lines
return {}; // vd must be annotated
double length = calculate_length(edge, lines);
double max_width = calculate_max_width(edge, lines);
coord_t min_width, max_width;
std::tie(min_width, max_width) = calculate_width(edge, lines);
auto neighbor_size = std::make_shared<VoronoiGraph::Node::Neighbor::Size>(
length, min_width, max_width);
VoronoiGraph::Node *node0 = getNode(skeleton, v0, &edge, lines);
VoronoiGraph::Node *node1 = getNode(skeleton, v1, &edge, lines);
// TODO: Do not store twice length and max_width.
// add extended Edge to graph, both side
VoronoiGraph::Node::Neighbor neighbor0(&edge, node1, length, max_width);
node0->neighbors.push_back(neighbor0);
VoronoiGraph::Node::Neighbor neighbor1(edge.twin(), node0, length, max_width);
node1->neighbors.push_back(neighbor1);
node0->neighbors.emplace_back(&edge, node1, neighbor_size);
node1->neighbors.emplace_back(edge.twin(), node0, std::move(neighbor_size));
}
return skeleton;
}
@ -485,7 +558,7 @@ double VoronoiGraphUtils::get_neighbor_distance(const VoronoiGraph::Node *from,
{
const VoronoiGraph::Node::Neighbor *neighbor = get_neighbor(from, to);
assert(neighbor != nullptr);
return neighbor->edge_length;
return neighbor->length();
}
VoronoiGraph::Path VoronoiGraphUtils::find_longest_path_on_circle(
@ -608,7 +681,7 @@ VoronoiGraph::Path VoronoiGraphUtils::find_longest_path_on_circles(
continue; // out of circles
if (done.find(neighbor.node) != done.end()) continue;
VoronoiGraph::Path neighbor_path = path; // make copy
neighbor_path.append(neighbor.node, neighbor.edge_length);
neighbor_path.append(neighbor.node, neighbor.length());
search_queue.push(neighbor_path);
auto branches_item = ex_path.side_branches.find(neighbor.node);
@ -655,7 +728,7 @@ std::optional<VoronoiGraph::Circle> VoronoiGraphUtils::create_circle(
VoronoiGraph::Nodes circle_path(path_item, passed_nodes.end());
// !!! Real circle lenght is calculated on detection of end circle
// now circle_length contain also lenght of path before circle
double circle_length = path.length + neighbor.edge_length;
double circle_length = path.length + neighbor.length();
// solve of branch length will be at begin of cirlce
return VoronoiGraph::Circle(std::move(circle_path), circle_length);
};
@ -933,12 +1006,12 @@ VoronoiGraph::Position VoronoiGraphUtils::align(
};
std::queue<NodeDistance> process;
const VoronoiGraph::Node::Neighbor* neighbor = position.neighbor;
double from_distance = neighbor->edge_length * position.ratio;
double from_distance = neighbor->length() * position.ratio;
if (from_distance < max_distance) {
const VoronoiGraph::Node *from_node = VoronoiGraphUtils::get_twin_node(neighbor);
process.emplace(from_node, from_distance);
}
double to_distance = neighbor->edge_length * (1 - position.ratio);
double to_distance = neighbor->length() * (1 - position.ratio);
if (to_distance < max_distance) {
const VoronoiGraph::Node *to_node = neighbor->node;
process.emplace(to_node, to_distance);
@ -966,7 +1039,7 @@ VoronoiGraph::Position VoronoiGraphUtils::align(
closest_distance = distance;
closest = VoronoiGraph::Position(&neighbor, ratio);
}
double from_start = nd.distance + neighbor.edge_length;
double from_start = nd.distance + neighbor.length();
if (from_start < max_distance)
process.emplace(neighbor.node, from_start);
}
@ -1013,9 +1086,9 @@ const VoronoiGraph::Node *VoronoiGraphUtils::getFirstContourNode(
return nullptr;
}
double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path)
coord_t VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path)
{
double max = 0.;
coord_t max = 0;
const VoronoiGraph::Node *prev_node = nullptr;
for (const VoronoiGraph::Node *node : path) {
if (prev_node == nullptr) {
@ -1023,16 +1096,16 @@ double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Nodes &path)
continue;
}
const VoronoiGraph::Node::Neighbor *neighbor = get_neighbor(prev_node, node);
if (max < neighbor->max_width) max = neighbor->max_width;
if (max < neighbor->max_width()) max = neighbor->max_width();
prev_node = node;
}
return max;
}
double VoronoiGraphUtils::get_max_width(
coord_t VoronoiGraphUtils::get_max_width(
const VoronoiGraph::ExPath &longest_path)
{
double max = get_max_width(longest_path.nodes);
coord_t max = get_max_width(longest_path.nodes);
for (const auto &side_branches_item : longest_path.side_branches) {
const VoronoiGraph::Node *prev_node = side_branches_item.first;
VoronoiGraph::ExPath::SideBranches side_branches = side_branches_item.second; // !!! copy
@ -1040,8 +1113,8 @@ double VoronoiGraphUtils::get_max_width(
const VoronoiGraph::Path &side_path = side_branches.top();
const VoronoiGraph::Node::Neighbor *first_neighbor =
get_neighbor(prev_node, side_path.nodes.front());
double max_side_branch = std::max(
get_max_width(side_path.nodes), first_neighbor->max_width);
coord_t max_side_branch = std::max(
get_max_width(side_path.nodes), first_neighbor->max_width());
if (max < max_side_branch) max = max_side_branch;
side_branches.pop();
}
@ -1051,7 +1124,7 @@ double VoronoiGraphUtils::get_max_width(
const VoronoiGraph::Node::Neighbor *first_neighbor =
get_neighbor(circle.nodes.front(), circle.nodes.back());
double max_circle = std::max(
first_neighbor->max_width, get_max_width(circle.nodes));
first_neighbor->max_width(), get_max_width(circle.nodes));
if (max < max_circle) max = max_circle;
}
@ -1059,9 +1132,9 @@ double VoronoiGraphUtils::get_max_width(
}
// !!! is slower than go along path
double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node)
coord_t VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node)
{
double max = 0.;
coord_t max = 0;
std::set<const VoronoiGraph::Node *> done;
std::queue<const VoronoiGraph::Node *> process;
process.push(node);
@ -1072,7 +1145,7 @@ double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node)
for (const VoronoiGraph::Node::Neighbor& neighbor: actual_node->neighbors) {
if (done.find(neighbor.node) != done.end()) continue;
process.push(neighbor.node);
if (max < neighbor.max_width) max = neighbor.max_width;
if (max < neighbor.max_width()) max = neighbor.max_width();
}
done.insert(actual_node);
}
@ -1081,13 +1154,16 @@ double VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node)
void VoronoiGraphUtils::draw(SVG & svg,
const VoronoiGraph &graph,
const Lines & lines,
coord_t width,
bool pointer_caption)
bool pointer_caption)
{
LineUtils::draw(svg, lines, "black", 0., true);
auto print_address = [&](const Point& p, const char* prefix, void * addr, const char* color){
if (pointer_caption) {
std::stringstream ss;
ss << prefix << std::hex << (int) addr;
ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr);
std::string s = ss.str();
svg.draw_text(p, s.c_str(), color);
}
@ -1096,7 +1172,7 @@ void VoronoiGraphUtils::draw(SVG & svg,
for (const auto &[key, value] : graph.data) {
Point p(key->x(), key->y());
svg.draw(p, "lightgray", width);
print_address(p, "v_",(void*)key, "lightgray");
print_address(p, "vertex ptr ",(void*)key, "lightgray");
for (const auto &n : value.neighbors) {
Point from = to_point(n.edge->vertex0());
Point to = to_point(n.edge->vertex1());
@ -1104,17 +1180,34 @@ void VoronoiGraphUtils::draw(SVG & svg,
Point center = (from + to) / 2;
Point p = center + ((is_second) ? Point(0., -2e6) :
Point(0., 2e6));
print_address(p, "n_", (void *) &n, "gray");
print_address(p, "neighbor ptr ", (void *) &n, "gray");
if (is_second) continue;
svg.draw_text(center + Point(-6e6, 0.), ("w="+std::to_string(n.max_width)).c_str(), "gray");
svg.draw(Line(from, to), "gray", width);
// svg.draw_text(center,
// (std::to_string(std::round(n.edge_length/3e5)/100.)).c_str(), "gray");
std::string width_str = "width min=" + std::to_string(n.min_width()) +
" max=" + std::to_string(n.max_width());
svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), "gray");
draw(svg, *n.edge, lines, "gray", width);
}
}
}
void VoronoiGraphUtils::draw(SVG & svg,
const VD::edge_type &edge,
const Lines & lines,
const char * color,
coord_t width)
{
Point from = to_point(edge.vertex0());
Point to = to_point(edge.vertex1());
if (edge.is_curved()) {
Parabola p = get_parabola(edge, lines);
ParabolaSegment ps(p, from, to);
ParabolaUtils::draw(svg, ps, color, width);
return;
}
svg.draw(Line(from, to), color, width);
}
void VoronoiGraphUtils::draw(SVG & svg,
const VoronoiGraph::Nodes &path,
coord_t width,

View File

@ -162,6 +162,7 @@ public:
/// <returns>Point from source points.</returns>
static const Point& retrieve_point(const Points &points, const VD::cell_type &cell);
private:
/// <summary>
/// PRIVATE: function to get parabola focus point
/// </summary>
@ -171,13 +172,14 @@ public:
static Point get_parabola_point(const VD::edge_type &parabola, const Lines &lines);
/// <summary>
/// PRIVATE:
/// PRIVATE: function to get parabola diretrix
/// </summary>
/// <param name="parabola"></param>
/// <param name="lines"></param>
/// <returns></returns>
/// <param name="parabola">curved edge</param>
/// <param name="lines">source lines</param>
/// <returns>Parabola diretrix</returns>
static Line get_parabola_line(const VD::edge_type &parabola, const Lines &lines);
public:
/// <summary>
/// Construct parabola from curved edge
/// </summary>
@ -204,12 +206,31 @@ public:
/// <summary>
/// Calculate maximal distance to outline and multiply by two(must be similar on both side)
/// ! not used
/// </summary>
/// <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);
/// <summary>
/// Calculate width limit(min, max) and round value to coord_t
/// </summary>
/// <param name="edge">Input edge</param>
/// <param name="lines">Source for Voronoi diagram. It contains parabola parameters</param>
/// <returns>Width range for edge.
/// First is minimal width on edge.
/// Second is maximal width on edge.</returns>
static std::pair<coord_t, coord_t> calculate_width(const VD::edge_type &edge, const Lines &lines);
private:
static std::pair<coord_t, coord_t> calculate_width_for_line(
const VD::edge_type &line_edge, const Lines &lines);
static std::pair<coord_t, coord_t> calculate_width_for_parabola(
const VD::edge_type &parabola_edge, const Lines &lines);
static std::pair<coord_t, coord_t> min_max_width(const VD::edge_type &edge, const Point &point);
public:
/// <summary>
/// calculate distances to border of island and length on skeleton
/// </summary>
@ -405,9 +426,9 @@ public:
/// </summary>
/// <param name="longest_path">Input point to voronoi graph</param>
/// <returns>Maximal widht in graph</returns>
static double get_max_width(const VoronoiGraph::ExPath &longest_path);
static double get_max_width(const VoronoiGraph::Nodes &path);
static double get_max_width(const VoronoiGraph::Node *node);
static coord_t get_max_width(const VoronoiGraph::ExPath &longest_path);
static coord_t get_max_width(const VoronoiGraph::Nodes &path);
static coord_t get_max_width(const VoronoiGraph::Node *node);
/// <summary>
/// Check if neighbor is end of VG
@ -417,7 +438,16 @@ public:
static bool is_last_neighbor(const VoronoiGraph::Node::Neighbor *neighbor);
public: // draw function for debug
static void draw(SVG &svg, const VoronoiGraph &graph, coord_t width, bool pointer_caption = false);
static void draw(SVG & svg,
const VoronoiGraph &graph,
const Lines & lines,
coord_t width,
bool pointer_caption = false);
static void draw(SVG & svg,
const VD::edge_type &edge,
const Lines & lines,
const char * color,
coord_t width);
static void draw(SVG & svg,
const VoronoiGraph::Nodes &path,
coord_t width,

View File

@ -218,7 +218,23 @@ Slic3r::Polygon create_V_shape(double height, double line_width, double angle =
return polygons.front();
}
ExPolygon create_tiny_wide_test(double wide, double tiny)
ExPolygon create_tiny_wide_test_1(double wide, double tiny)
{
double hole_size = wide;
double width = 2 * wide + hole_size;
double height = wide + hole_size + tiny;
auto outline = PolygonUtils::create_rect(width, height);
auto hole = PolygonUtils::create_rect(hole_size, hole_size);
hole.reverse();
int hole_move_y = height/2 - (hole_size/2 + tiny);
hole.translate(0, hole_move_y);
ExPolygon result(outline);
result.holes = {hole};
return result;
}
ExPolygon create_tiny_wide_test_2(double wide, double tiny)
{
double hole_size = wide;
double width = (3 + 1) * wide + 3 * hole_size;
@ -290,7 +306,9 @@ ExPolygons createTestIslands(double size)
create_square_with_4holes(5 * size, 5 * size / 2 - size / 3),
// Tiny and wide part together with holes
create_tiny_wide_test(3 * size, 2 / 3. * size),
ExPolygon(PolygonUtils::create_isosceles_triangle(5. * size, 40. * size)),
create_tiny_wide_test_1(3 * size, 2 / 3. * size),
create_tiny_wide_test_2(3 * size, 2 / 3. * size),
// still problem
// three support points