Chenge is_last_neighbor to end_in_distance

This commit is contained in:
Filip Sykala - NTB T15p 2024-09-19 14:49:37 +02:00 committed by Lukas Matena
parent d26f0358fe
commit 7216e819b3
6 changed files with 184 additions and 124 deletions

View File

@ -70,7 +70,12 @@ struct SampleConfig
coord_t outline_sample_distance = 2;
// Maximal distance over Voronoi diagram edges to find closest point during aligning Support point
coord_t max_align_distance = 0.;
coord_t max_align_distance = 0; // [nano meter]
// There is no need to calculate with precisse island
// NOTE: Slice of Cylinder bottom has tip of trinagles on contour
// (neighbor coordinate - create issue in voronoi)
double simplification_tolerance = 1e4; // [nm]
};
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_

View File

@ -17,10 +17,9 @@ public:
static SampleConfig create(const SupportPointGenerator::Config &config)
{
coord_t head_diameter = scale_(config.head_diameter);
coord_t min_distance = scale_(config.minimal_distance);
coord_t min_distance = head_diameter/2 + scale_(config.minimal_distance);
coord_t max_distance = 3 * min_distance;
coord_t sample_multiplicator = 10; // allign is made by selecting from samples
coord_t sample_multiplicator = 8; // allign is made by selecting from samples
// TODO: find valid params !!!!
SampleConfig result;

View File

@ -250,9 +250,10 @@ SupportIslandPoints SampleIslandUtils::create_side_points(
{
VoronoiGraph::Nodes reverse_path = path; // copy
std::reverse(reverse_path.begin(), reverse_path.end());
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);
coord_t side_distance1 = max_side_distance; // copy
coord_t side_distance2 = max_side_distance; // copy
auto pos1 = create_position_on_path(path, lines, width, side_distance1);
auto pos2 = create_position_on_path(reverse_path, lines, width, side_distance2);
assert(pos1.has_value());
assert(pos2.has_value());
SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points;
@ -423,7 +424,7 @@ void SampleIslandUtils::align_samples(SupportIslandPoints &samples,
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG
static int counter = 0;
SVG svg(("aligned_" + std::to_string(counter++) + ".svg").c_str(),BoundingBox(island));
SVG svg(("aligned_" + std::to_string(counter++) + ".svg").c_str(),BoundingBox(island.contour.points));
svg.draw(island);
draw(svg, samples, config.head_radius);
svg.Close();
@ -463,7 +464,7 @@ coord_t SampleIslandUtils::align_once(SupportIslandPoints &samples,
std::string color_new_point = "blue"; // Center of island cell intersection
std::string color_static_point = "black";
static int counter = 0;
BoundingBox bbox(island);
BoundingBox bbox(island.contour.points);
SVG svg(("align_" + std::to_string(counter++) + ".svg").c_str(), bbox);
svg.draw(island, color_of_island );
@ -888,9 +889,9 @@ SupportIslandPoints SampleIslandUtils::sample_voronoi_graph(
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_VORONOI_GRAPH_TO_SVG
{
static int counter = 0;
SVG svg("voronoiGraph" + std::to_string(counter++) + ".svg",
SVG svg("voronoiGraph" + std::to_string(counter++) + ".svg",
LineUtils::create_bounding_box(lines));
VoronoiGraphUtils::draw(svg, graph, lines, 1e6, true);
VoronoiGraphUtils::draw(svg, graph, lines, config.head_radius / 10, true);
}
#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_VORONOI_GRAPH_TO_SVG
@ -924,13 +925,16 @@ 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
// 2) Two support points have to stretch island even if haed is not fully under island.
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);
coord_t max_distance_by_length = static_cast<coord_t>(path.length / 4);
coord_t max_distance = std::min(config.half_distance, max_distance_by_length);
// Be carefull tiny island could contain overlapped support points
assert(max_distance < (static_cast<coord_t>(path.length / 2) - config.head_radius));
coord_t min_width = 2 * config.head_radius; //minimal_distance_from_outline;
return create_side_points(path.nodes, lines, min_width, max_distance);
}
// othewise sample path
@ -995,7 +999,7 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
return points;
}
void SampleIslandUtils::sample_field(VoronoiGraph::Position &field_start,
void SampleIslandUtils::sample_field(const VoronoiGraph::Position &field_start,
SupportIslandPoints & points,
CenterStarts & center_starts,
std::set<const VoronoiGraph::Node *> &done,
@ -1029,13 +1033,11 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
const Lines & lines,
const SampleConfig & config)
{
const VoronoiGraph::Node::Neighbor *neighbor = nullptr;
VoronoiGraph::Nodes path;
coord_t support_in;
CenterStart start(nullptr, {}, {});
bool use_new_start = true;
bool is_continous = false;
while (use_new_start || neighbor->max_width() <= config.max_width_for_center_support_line) {
while (use_new_start || 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;
@ -1046,31 +1048,28 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
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;
start = std::move(new_starts.back());
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 / neighbor->length();
VoronoiGraph::Position position(neighbor, ratio);
coord_t edge_length = static_cast<coord_t>(start.neighbor->length());
while (edge_length >= start.support_in) {
double ratio = start.support_in / start.neighbor->length();
VoronoiGraph::Position position(start.neighbor, ratio);
results.push_back(std::make_unique<SupportCenterIslandPoint>(
position, &config, SupportIslandPoint::Type::center_line1));
support_in += config.max_distance;
start.support_in += config.max_distance;
is_continous = true;
}
support_in -= edge_length;
start.support_in -= edge_length;
const VoronoiGraph::Node *node = neighbor->node;
const VoronoiGraph::Node *node = start.neighbor->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);
start.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;
@ -1078,13 +1077,13 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
next_neighbor = &node_neighbor;
continue;
}
new_starts.emplace_back(&node_neighbor, support_in, path); // search in side branch
new_starts.emplace_back(&node_neighbor, start.support_in, start.path); // search in side branch
}
if (next_neighbor == nullptr) {
if (neighbor->min_width() != 0) {
std::reverse(path.begin(), path.end());
auto position_opt = create_position_on_path(path, support_in / 2);
if (start.neighbor->min_width() != 0) {
std::reverse(start.path.begin(), start.path.end());
auto position_opt = create_position_on_path(start.path, start.support_in / 2);
if (position_opt.has_value()) {
results.push_back(
std::make_unique<SupportCenterIslandPoint>(
@ -1093,29 +1092,30 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
}
} else {
// no neighbor to continue
create_sample_center_end(*neighbor, is_continous, path,
support_in, lines, results,
create_sample_center_end(*start.neighbor, is_continous, start.path,
start.support_in, lines, results,
new_starts, config);
}
use_new_start = true;
} else {
neighbor = next_neighbor;
start.neighbor = next_neighbor;
}
}
// create field start
auto result = VoronoiGraphUtils::get_position_with_width(
neighbor, config.min_width_for_outline_support, lines);
start.neighbor, config.min_width_for_outline_support, lines
);
// sample rest of neighbor before field
double edge_length = neighbor->length();
double edge_length = start.neighbor->length();
double sample_length = edge_length * result.ratio;
while (sample_length > support_in) {
double ratio = support_in / edge_length;
VoronoiGraph::Position position(neighbor, ratio);
while (sample_length > start.support_in) {
double ratio = start.support_in / edge_length;
VoronoiGraph::Position position(start.neighbor, ratio);
results.push_back(std::make_unique<SupportCenterIslandPoint>(
position, &config, SupportIslandPoint::Type::center_line2));
support_in += config.max_distance;
start.support_in += config.max_distance;
}
return result;
}
@ -1140,22 +1140,25 @@ void SampleIslandUtils::create_sample_center_end(
// exist place for support?
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);
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()) return;
// check if exist popable result
if (is_continous && config.max_distance < (support_in + distance)) {
if(!create_sample_center_end(*position_opt, results, new_starts, config))
return;
// check if exist unneccesary support point before no move end
if (is_continous && config.max_distance < (support_in + distance) && results.size()>2) {
// one support point should be enough
// when max_distance > maximal_distance_from_outline
results.pop_back(); // remove support point
// one before last is not needed
results.erase(results.end() - 2);// remove support point
}
create_sample_center_end(*position_opt, results, new_starts, config);
}
void SampleIslandUtils::create_sample_center_end(
bool SampleIslandUtils::create_sample_center_end(
const VoronoiGraph::Position &position,
SupportIslandPoints & results,
CenterStarts & new_starts,
@ -1172,61 +1175,54 @@ void SampleIslandUtils::create_sample_center_end(
Point diff = point - res->point;
if (abs(diff.x()) > minimal_support_distance) continue;
if (abs(diff.y()) > minimal_support_distance) continue;
// do not add overlapping end point
if (diff.x() < config.head_radius &&
diff.y() < config.head_radius) return false;
// create raw pointer, used only in function scope
near_no_move.push_back(&*res);
}
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()) {
std::function<void(const VoronoiGraph::Node::Neighbor &, coord_t)>
collect_distances = [&distances](const auto &neighbor, coord_t act_distance) {
distances[&neighbor] = act_distance;
};
VoronoiGraphUtils::for_neighbor_at_distance(position, minimal_support_distance, collect_distances);
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, 10000)) {
exist_no_move = true;
break;
}
if (LineUtils::belongs(edge, support_point->point, 10000))
return false;
}
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);
// 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 - support_distance;
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);
item = distances.find(twin);
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);
}
coord_t support_distance = item->second + static_cast<coord_t>(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));
}
results.push_back(std::make_unique<SupportIslandNoMovePoint>(point, no_move_type));
return true;
}
@ -1238,7 +1234,6 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
const SampleConfig &config)
{
using VD = Slic3r::Geometry::VoronoiDiagram;
const coord_t min_width = config.min_width_for_outline_support;
// DTO represents one island change from wide to tiny part
// it is stored inside map under source line index
@ -1269,31 +1264,26 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
};
};
using WideTinyChanges = std::vector<WideTinyChange>;
// store shortening of outline segments
// line index, vector<next line index + 2x shortening points>
std::map<size_t, WideTinyChanges> wide_tiny_changes;
coord_t minimal_edge_length = std::max(config.max_distance / 2, 2*config.minimal_distance_from_outline);
coord_t half_max_distance = config.max_distance / 2;
// 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::Position &position,
const VoronoiGraph::Node * source_node)->bool {
const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor;
// 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) &&
rest_size <= minimal_edge_length)
[minimal_edge_length, half_max_distance, &wide_tiny_changes,
&lines, &tiny_starts, &tiny_done]
(const VoronoiGraph::Position &position, const VoronoiGraph::Node *source_node)->bool{
if (VoronoiGraphUtils::ends_in_distanace(position, minimal_edge_length))
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
auto add = [&](const Point &p1, const Point &p2, size_t i1,
size_t i2) {
auto add = [&](const Point &p1, const Point &p2, size_t i1, size_t i2) {
WideTinyChange change(p1, p2, i2);
auto item = wide_tiny_changes.find(i1);
auto item = wide_tiny_changes.find(i1);
if (item == wide_tiny_changes.end()) {
wide_tiny_changes[i1] = {change};
} else {
@ -1304,26 +1294,27 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
Point p1, p2;
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines);
const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor;
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];
if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) {
// line1 is shorten on side line1.a --> line2 is shorten on
// side line2.b
// line1 is shorten on side line1.a --> line2 is shorten on side line2.b
add(p2, p1, i2, i1);
} else {
// line1 is shorten on side line1.b
add(p1, p2, i1, i2);
}
coord_t support_in = neighbor->length() * position.ratio + config.max_distance/2;
coord_t support_in = neighbor->length() * position.ratio + half_max_distance;
CenterStart tiny_start(neighbor, support_in, {source_node});
tiny_starts.push_back(tiny_start);
tiny_done.insert(source_node);
return true;
};
const coord_t min_width = config.min_width_for_outline_support;
const VoronoiGraph::Node::Neighbor *tiny_wide_neighbor = field_start.neighbor;
const VoronoiGraph::Node::Neighbor *wide_tiny_neighbor = VoronoiGraphUtils::get_twin(*tiny_wide_neighbor);
VoronoiGraph::Position position(wide_tiny_neighbor, 1. - field_start.ratio);
@ -1380,7 +1371,8 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
LineUtils::create_line_connection_over_b(lines);
std::vector<size_t> source_indexes;
auto inser_point_b = [&](size_t& index, Points& points, std::set<size_t>& done)
auto inser_point_b = [&lines, &b_connection, &source_indexes]
(size_t &index, Points &points, std::set<size_t> &done)
{
const Line &line = lines[index];
points.push_back(line.b);
@ -1392,14 +1384,15 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
};
size_t source_indexe_for_change = lines.size();
auto insert_changes = [&](size_t &index, Points &points, std::set<size_t> &done, size_t input_index)->bool {
auto insert_changes = [&wide_tiny_changes, &lines, &source_indexes, source_indexe_for_change]
(size_t &index, Points &points, std::set<size_t> &done, size_t input_index)->bool {
bool is_first = points.empty();
auto change_item = wide_tiny_changes.find(index);
while (change_item != wide_tiny_changes.end()) {
const WideTinyChanges &changes = change_item->second;
assert(!changes.empty());
size_t change_index = 0;
if (!points.empty()) {
if (!is_first) {
const Point & last_point = points.back();
LineUtils::SortFromAToB pred(lines[index]);
bool no_change = false;
@ -1413,11 +1406,9 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
if (no_change) break;
// Field ends with change into first index
if (!is_first && change_item->first == input_index &&
if (change_item->first == input_index &&
change_index == 0) {
return false;
} else {
is_first = false;
}
}
const WideTinyChange &change = changes[change_index];
@ -1442,6 +1433,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
return true;
};
// Collect outer points of field
Points points;
points.reserve(field_line_indexes.size());
std::vector<size_t> outline_indexes;

View File

@ -304,7 +304,8 @@ private:
/// <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(
/// <returns>True when add point into result otherwise false</returns>
static bool create_sample_center_end(
const VoronoiGraph::Position &position,
SupportIslandPoints & results,
CenterStarts & new_starts,
@ -341,7 +342,7 @@ public :
/// <param name="tiny_done">Already sampled node sets. Filled only node inside field imediate after change</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>
static void sample_field(VoronoiGraph::Position &field_start,
static void sample_field(const VoronoiGraph::Position &field_start,
SupportIslandPoints & points,
CenterStarts & center_starts,
std::set<const VoronoiGraph::Node *> &done,

View File

@ -922,6 +922,8 @@ Slic3r::Point VoronoiGraphUtils::create_edge_point(const VD::edge_type *edge,
return Point(v0->x() + dir.x(), v0->y() + dir.y());
}
// NOTE: Heuristic is bad -> Width is not linear on edge e.g. VD of hexagon
// Solution: Edge has to know width changes.
VoronoiGraph::Position VoronoiGraphUtils::get_position_with_width(
const VoronoiGraph::Node::Neighbor *neighbor, coord_t width, const Slic3r::Lines &lines)
{
@ -1164,10 +1166,70 @@ coord_t VoronoiGraphUtils::get_max_width(const VoronoiGraph::Node *node)
return max;
}
bool VoronoiGraphUtils::is_last_neighbor(
const VoronoiGraph::Node::Neighbor *neighbor)
{
return (neighbor->node->neighbors.size() == 1);
// START use instead of is_last_neighbor
bool VoronoiGraphUtils::ends_in_distanace(const VoronoiGraph::Position &position, coord_t max_distance) {
const VoronoiGraph::Node *node = position.neighbor->node;
coord_t rest_distance = max_distance - position.calc_rest_distance();
if (rest_distance < 0)
return false;
// speed up - end of gpraph is no need investigate further
if (node->neighbors.size() == 1)
return true;
// Already processed nodes
std::set<const VoronoiGraph::Node *> done;
done.insert(get_twin_node(*position.neighbor));
struct Next{
const VoronoiGraph::Node *node;
coord_t rest_distance;
};
// sorted by distance from position from biggest
std::vector<Next> process_queue;
do {
done.insert(node);
for (const VoronoiGraph::Node::Neighbor &neighbor: node->neighbors){
const VoronoiGraph::Node *neighbor_node = neighbor.node;
// Check whether node is already done
// Nodes are processed from closer to position
// soo done neighbor have to has bigger rest_distance
if (done.find(neighbor_node) != done.end())
// node is already explore
continue;
coord_t neighbor_rest = rest_distance - static_cast<coord_t>(neighbor.length());
if (neighbor_rest < 0)
// exist node far than max distance
return false;
// speed up - end of gpraph is no need to add to the process queue
if (neighbor_node->neighbors.size() == 1)
continue;
// check whether exist in queue this node with farer path and fix it
auto it = std::find_if(process_queue.begin(), process_queue.end(),
[neighbor_node](const Next &n) { return n.node == neighbor_node;});
if (it == process_queue.end()){
process_queue.emplace_back(Next{neighbor_node, neighbor_rest});
} else if (it->rest_distance < neighbor_rest) {
// found shorter path to node
it->rest_distance = neighbor_rest;
}
}
if (process_queue.empty())
return true;
// find biggest rest distance -> closest to input position
auto next = std::max_element(process_queue.begin(), process_queue.end(),
[](const Next& n1, const Next& n2){
return n1.rest_distance < n2.rest_distance;
});
rest_distance = next->rest_distance;
node = next->node;
process_queue.erase(next); // process queue pop
} while (true);
}
void VoronoiGraphUtils::for_neighbor_at_distance(

View File

@ -448,11 +448,12 @@ public:
static coord_t get_max_width(const VoronoiGraph::Node *node);
/// <summary>
/// Check if neighbor is end of VG
/// Check wheather VG ends in smaller distance than given one
/// </summary>
/// <param name="neighbor">Neighbor to test</param>
/// <returns>True when neighbor node has only one neighbor</returns>
static bool is_last_neighbor(const VoronoiGraph::Node::Neighbor *neighbor);
/// <param name="position">Position in direction to checked end</param>
/// <param name="max_distance">distance to explore</param>
/// <returns>True when there is only smaller VD path to edge</returns>
static bool ends_in_distanace(const VoronoiGraph::Position &position, coord_t max_distance);
/// <summary>
/// only line created VG