Fix for sampling and enhance visualization.

This commit is contained in:
Filip Sykala - NTB T15p 2024-10-24 14:31:54 +02:00 committed by Lukas Matena
parent 73f8583e18
commit 688c614e47
6 changed files with 126 additions and 65 deletions

View File

@ -124,9 +124,10 @@ SVG draw_island(const std::string &path, const ExPolygon &island, const ExPolygo
} }
SVG draw_island_graph(const std::string &path, const ExPolygon &island, SVG draw_island_graph(const std::string &path, const ExPolygon &island,
const ExPolygon &simplified_island, const VoronoiGraph& skeleton, const ExPolygon &simplified_island, const VoronoiGraph& skeleton,
const VoronoiGraph::ExPath& longest_path, const Lines& lines, coord_t width) { const VoronoiGraph::ExPath& longest_path, const Lines& lines, const SampleConfig &config) {
SVG svg = draw_island(path, island, simplified_island); SVG svg = draw_island(path, island, simplified_island);
VoronoiGraphUtils::draw(svg, skeleton, lines, width); VoronoiGraphUtils::draw(svg, skeleton, lines, config, true /*print Pointer address*/);
coord_t width = config.head_radius / 10;
VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "orange"); VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "orange");
return svg; return svg;
} }
@ -176,7 +177,7 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
longest_path = VoronoiGraphUtils::create_longest_path(start_node); longest_path = VoronoiGraphUtils::create_longest_path(start_node);
#ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image #ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image
if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10); if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
#endif // OPTION_TO_STORE_ISLAND #endif // OPTION_TO_STORE_ISLAND
SupportIslandPoints samples = sample_expath(longest_path, lines, config); SupportIslandPoints samples = sample_expath(longest_path, lines, config);
@ -184,7 +185,7 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
#ifdef OPTION_TO_STORE_ISLAND #ifdef OPTION_TO_STORE_ISLAND
Points samples_before_align = to_points(samples); Points samples_before_align = to_points(samples);
if (!path.empty()) { if (!path.empty()) {
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10); SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
draw(svg, samples, config.head_radius); draw(svg, samples, config.head_radius);
} }
#endif // OPTION_TO_STORE_ISLAND #endif // OPTION_TO_STORE_ISLAND
@ -193,13 +194,17 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
SampleIslandUtils::align_samples(samples, island, config); SampleIslandUtils::align_samples(samples, island, config);
#ifdef OPTION_TO_STORE_ISLAND #ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()) { if (!path.empty()) {
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10); SVG svg = draw_island(path, island, simplified_island);
draw(svg, samples, config.head_radius); coord_t width = config.head_radius / 5;
VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "darkorange");
VoronoiGraphUtils::draw(svg, skeleton, lines, config, false /*print Pointer address*/);
Lines align_moves; Lines align_moves;
align_moves.reserve(samples.size()); align_moves.reserve(samples.size());
for (size_t i = 0; i < samples.size(); ++i) for (size_t i = 0; i < samples.size(); ++i)
align_moves.push_back(Line(samples[i]->point, samples_before_align[i])); align_moves.push_back(Line(samples[i]->point, samples_before_align[i]));
svg.draw(align_moves, "lightgray"); svg.draw(align_moves, "lightgray", width);
draw(svg, samples, config.head_radius);
} }
#endif // OPTION_TO_STORE_ISLAND #endif // OPTION_TO_STORE_ISLAND
return samples; return samples;
@ -1130,7 +1135,6 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
coord_t support_in = config.max_distance + already_supported; coord_t support_in = config.max_distance + already_supported;
center_starts.emplace_back(position->neighbor, support_in, start_path); center_starts.emplace_back(position->neighbor, support_in, start_path);
} else { } else {
assert(position.has_value());
done.insert(start_node); done.insert(start_node);
coord_t support_in = config.minimal_distance_from_outline; coord_t support_in = config.minimal_distance_from_outline;
center_starts.emplace_back(neighbor, support_in); center_starts.emplace_back(neighbor, support_in);
@ -1263,16 +1267,15 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
std::set<const VoronoiGraph::Node *> &done, std::set<const VoronoiGraph::Node *> &done,
SupportIslandPoints & results, SupportIslandPoints & results,
const Lines & lines, const Lines & lines,
const SampleConfig & config) const SampleConfig & config,
// sign that there was added point on start.path
// used to distiquish whether add support point on island edge
bool is_continous)
{ {
// Current place to sample // Current place to sample
CenterStart start(nullptr, {}, {}); CenterStart start(nullptr, {}, {});
if (!pop_start(start, new_starts, done)) return {}; if (!pop_start(start, new_starts, done)) return {};
// sign that there was added point on start.path
// used to distiquish whether add support point on island edge
bool is_continous = false;
// Loop over thin part of island which need to be sampled on the voronoi skeleton. // Loop over thin part of island which need to be sampled on the voronoi skeleton.
while (!is_continous || start.neighbor->max_width() <= config.max_width_for_center_support_line) { while (!is_continous || start.neighbor->max_width() <= config.max_width_for_center_support_line) {
assert(done.find(start.neighbor->node) == done.end()); // not proccessed only assert(done.find(start.neighbor->node) == done.end()); // not proccessed only
@ -1489,16 +1492,16 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
// line index, vector<next line index + 2x shortening points> // line index, vector<next line index + 2x shortening points>
std::map<size_t, WideTinyChanges> wide_tiny_changes; 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); // Prepare data for field outline,
coord_t half_max_distance = config.max_distance / 2; // when field transit into tiny part of island
// cut lines at place where neighbor has width = min_width_for_outline_support auto add_wide_tiny_change_only = [&wide_tiny_changes, &lines, &tiny_done]
// neighbor must be in direction from wide part to tiny part of island (const VoronoiGraph::Position &position){
auto add_wide_tiny_change = Point p1, p2;
[minimal_edge_length, half_max_distance, &wide_tiny_changes, std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines);
&lines, &tiny_starts, &tiny_done] const VoronoiGraph::Node::Neighbor *neighbor = position.neighbor;
(const VoronoiGraph::Position &position, const VoronoiGraph::Node *source_node)->bool{ const VD::edge_type *edge = neighbor->edge;
if (VoronoiGraphUtils::ends_in_distanace(position, minimal_edge_length)) size_t i1 = edge->cell()->source_index();
return false; // no change only rich outline size_t i2 = edge->twin()->cell()->source_index();
// function to add sorted change from wide to tiny // function to add sorted change from wide to tiny
// stored uder line index or line shorten in point b // stored uder line index or line shorten in point b
@ -1513,13 +1516,6 @@ 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]; const Line &l1 = lines[i1];
if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) { 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
@ -1528,8 +1524,22 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
// line1 is shorten on side line1.b // line1 is shorten on side line1.b
add(p1, p2, i1, i2); add(p1, p2, i1, i2);
} }
coord_t support_in = neighbor->length() * position.ratio + half_max_distance; };
CenterStart tiny_start(neighbor, support_in, {source_node});
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 = [minimal_edge_length, half_max_distance,
add_wide_tiny_change_only, &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
add_wide_tiny_change_only(position);
coord_t support_in = position.neighbor->length() * position.ratio + half_max_distance;
CenterStart tiny_start(position.neighbor, support_in, {source_node});
tiny_starts.push_back(tiny_start); tiny_starts.push_back(tiny_start);
tiny_done.insert(source_node); tiny_done.insert(source_node);
return true; return true;
@ -1543,6 +1553,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
std::set<const VoronoiGraph::Node*> done; std::set<const VoronoiGraph::Node*> done;
done.insert(wide_tiny_neighbor->node); done.insert(wide_tiny_neighbor->node);
// prev node , node // prev node , node
using ProcessItem = std::pair<const VoronoiGraph::Node *, const VoronoiGraph::Node *>; using ProcessItem = std::pair<const VoronoiGraph::Node *, const VoronoiGraph::Node *>;
// initial proccess item from tiny part to wide part of island // initial proccess item from tiny part to wide part of island
@ -1575,7 +1586,18 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
if(add_wide_tiny_change(position, node)) if(add_wide_tiny_change(position, node))
continue; continue;
} }
if (done.find(neighbor.node) != done.end()) continue; // loop back if (done.find(neighbor.node) != done.end()) continue; // loop back into field
// Detection that wide part do not continue over already sampled tiny part
// Caused by histereze of wide condition.
if (auto it = std::find_if(tiny_starts.begin(), tiny_starts.end(),
[twin=VoronoiGraphUtils::get_twin(neighbor)](const SampleIslandUtils::CenterStart& start)->bool{
return twin == start.neighbor; });
it != tiny_starts.end()) {
add_wide_tiny_change_only(VoronoiGraph::Position(&neighbor, 1.));
tiny_starts.erase(it);
continue;
}
if (next_node == nullptr) { if (next_node == nullptr) {
next_node = neighbor.node; next_node = neighbor.node;
} else { } else {
@ -1993,10 +2015,10 @@ void SampleIslandUtils::draw(SVG & svg,
void SampleIslandUtils::draw(SVG & svg, void SampleIslandUtils::draw(SVG & svg,
const SupportIslandPoints &supportIslandPoints, const SupportIslandPoints &supportIslandPoints,
double size, coord_t radius,
const char * color,
bool write_type) bool write_type)
{ {
const char *color = nullptr;
for (const auto &p : supportIslandPoints) { for (const auto &p : supportIslandPoints) {
switch (p->type) { switch (p->type) {
case SupportIslandPoint::Type::center_line1: case SupportIslandPoint::Type::center_line1:
@ -2016,11 +2038,11 @@ void SampleIslandUtils::draw(SVG & svg,
case SupportIslandPoint::Type::two_points: case SupportIslandPoint::Type::two_points:
default: color = "black"; default: color = "black";
} }
svg.draw(p->point, color, size); svg.draw(p->point, color, radius);
if (write_type && p->type != SupportIslandPoint::Type::undefined) { if (write_type && p->type != SupportIslandPoint::Type::undefined) {
auto type_name = SupportIslandPoint::to_string(p->type); auto type_name = SupportIslandPoint::to_string(p->type);
Point start = p->point + Point(size, 0.); Point start = p->point + Point(radius, 0);
svg.draw_text(start, std::string(type_name).c_str(), color); svg.draw_text(start, std::string(type_name).c_str(), color, 8);
} }
} }
} }

View File

@ -267,13 +267,16 @@ public:
/// <param name="results">Result of sampling</param> /// <param name="results">Result of sampling</param>
/// <param name="lines">Source line for VD. To decide position of change from tiny to wide part</param> /// <param name="lines">Source line for VD. To decide position of change from tiny to wide part</param>
/// <param name="config">Parameters for sampling</param> /// <param name="config">Parameters for sampling</param>
/// <param name="is_continous">Already place sample on path</param>
/// <returns>Wide neighbor, start of field when exists</returns> /// <returns>Wide neighbor, start of field when exists</returns>
static std::optional<VoronoiGraph::Position> sample_center( static std::optional<VoronoiGraph::Position> sample_center(
CenterStarts & new_starts, CenterStarts & new_starts,
std::set<const VoronoiGraph::Node *> &done, std::set<const VoronoiGraph::Node *> &done,
SupportIslandPoints & results, SupportIslandPoints & results,
const Lines & lines, const Lines & lines,
const SampleConfig & config); const SampleConfig & config,
bool is_continous = false
);
private: private:
/// <summary> /// <summary>
@ -390,8 +393,7 @@ public :
static void draw(SVG & svg, static void draw(SVG & svg,
const SupportIslandPoints &supportIslandPoints, const SupportIslandPoints &supportIslandPoints,
double size, coord_t radius,
const char *color = "lightgreen",
bool write_type = true); bool write_type = true);
}; };

View File

@ -1301,20 +1301,45 @@ double VoronoiGraphUtils::outline_angle(const VoronoiGraph::Node::Neighbor &neig
void VoronoiGraphUtils::draw(SVG & svg, void VoronoiGraphUtils::draw(SVG & svg,
const VoronoiGraph &graph, const VoronoiGraph &graph,
const Lines & lines, const Lines & lines,
coord_t width, const SampleConfig &config,
bool pointer_caption) bool pointer_caption)
{ {
LineUtils::draw(svg, lines, "black", 0., true); coord_t width = config.head_radius / 10;
LineUtils::draw(svg, lines, "black", width, false);
auto print_address = [&](const Point& p, const char* prefix, void * addr, const char* color){ auto print_address = [&](const Point& p, const char* prefix, void * addr, const char* color){
if (pointer_caption) { if (pointer_caption) {
std::stringstream ss; std::stringstream ss;
ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr); ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr);
std::string s = ss.str(); std::string s = ss.str();
svg.draw_text(p, s.c_str(), color); svg.draw_text(p, s.c_str(), color, 6);
} }
}; };
std::vector<const char *> skeleton_colors{
"yellow", // thin (min+max belowe thin)
"yellowgreen", // on way to thin (max is above thin)
"limegreen", // between (inside histerezis)
"forestgreen", // on way to thick (min is belove thick)
"darkgreen" // thick (min+max above thick)
};
auto get_color = [&](const VoronoiGraph::Node::Neighbor &n) {
if (n.min_width() > config.max_width_for_center_support_line){
return skeleton_colors[4];
} else if (n.max_width() < config.min_width_for_outline_support){
return skeleton_colors[0];
} else if (n.min_width() < config.max_width_for_center_support_line &&
n.max_width() > config.min_width_for_outline_support){
return skeleton_colors[2];
} else if (n.min_width() < config.min_width_for_outline_support){
return skeleton_colors[1];
} else if (n.max_width() > config.max_width_for_center_support_line) {
return skeleton_colors[3];
}
assert(false);
return "gray";
};
for (const auto &[key, value] : graph.data) { for (const auto &[key, value] : graph.data) {
Point p(key->x(), key->y()); Point p(key->x(), key->y());
svg.draw(p, "lightgray", width); svg.draw(p, "lightgray", width);
@ -1328,10 +1353,13 @@ void VoronoiGraphUtils::draw(SVG & svg,
Point(0., 2e6)); Point(0., 2e6));
print_address(p, "neighbor ptr ", (void *) &n, "gray"); print_address(p, "neighbor ptr ", (void *) &n, "gray");
if (is_second) continue; if (is_second) continue;
std::string width_str = "width min=" + std::to_string(n.min_width()) + const char *color = get_color(n);
" max=" + std::to_string(n.max_width()); if (pointer_caption) {
svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), "gray"); std::string width_str = "width min=" + std::to_string(n.min_width()) +
draw(svg, *n.edge, lines, "gray", width); " max=" + std::to_string(n.max_width());
svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), color, 6);
}
draw(svg, *n.edge, lines, color, width);
} }
} }
} }
@ -1358,7 +1386,8 @@ void VoronoiGraphUtils::draw(SVG & svg,
const VoronoiGraph::Nodes &path, const VoronoiGraph::Nodes &path,
coord_t width, coord_t width,
const char * color, const char * color,
bool finish) bool finish,
bool caption)
{ {
const VoronoiGraph::Node *prev_node = (finish) ? path.back() : nullptr; const VoronoiGraph::Node *prev_node = (finish) ? path.back() : nullptr;
int index = 0; int index = 0;
@ -1372,9 +1401,10 @@ void VoronoiGraphUtils::draw(SVG & svg,
Point from = to_point(prev_node->vertex); Point from = to_point(prev_node->vertex);
Point to = to_point(node->vertex); Point to = to_point(node->vertex);
svg.draw(Line(from, to), color, width); svg.draw(Line(from, to), color, width);
if (caption) {
svg.draw_text(from, std::to_string(index - 1).c_str(), color); svg.draw_text(from, std::to_string(index - 1).c_str(), color, 6);
svg.draw_text(to, std::to_string(index).c_str(), color); svg.draw_text(to, std::to_string(index).c_str(), color, 6);
}
prev_node = node; prev_node = node;
} }
} }

View File

@ -479,7 +479,7 @@ public: // draw function for debug
static void draw(SVG & svg, static void draw(SVG & svg,
const VoronoiGraph &graph, const VoronoiGraph &graph,
const Lines & lines, const Lines & lines,
coord_t width, const SampleConfig &config,
bool pointer_caption = false); bool pointer_caption = false);
static void draw(SVG & svg, static void draw(SVG & svg,
const VD::edge_type &edge, const VD::edge_type &edge,
@ -490,7 +490,8 @@ public: // draw function for debug
const VoronoiGraph::Nodes &path, const VoronoiGraph::Nodes &path,
coord_t width, coord_t width,
const char * color, const char * color,
bool finish = false); bool finish = false,
bool caption = false);
static void draw(SVG & svg, static void draw(SVG & svg,
const VoronoiGraph::ExPath &path, const VoronoiGraph::ExPath &path,
coord_t width); coord_t width);

View File

@ -60,6 +60,8 @@ public:
return shape.contains(p); return shape.contains(p);
}); });
}); });
if (it == indices.end())
return; // no point to remove
indices.erase(it, indices.end()); indices.erase(it, indices.end());
m_tree.clear(); m_tree.clear();
m_tree.build(indices); // consume indices m_tree.build(indices); // consume indices
@ -235,8 +237,7 @@ void support_part_overhangs(
Point dp = support_point.position_on_layer - p; Point dp = support_point.position_on_layer - p;
if (std::abs(dp.x()) > r) return false; if (std::abs(dp.x()) > r) return false;
if (std::abs(dp.y()) > r) return false; if (std::abs(dp.y()) > r) return false;
double r2 = static_cast<double>(r); double r2 = sqr(static_cast<double>(r));
r2 *= r2;
return dp.cast<double>().squaredNorm() < r2; return dp.cast<double>().squaredNorm() < r2;
}; };
@ -246,7 +247,7 @@ void support_part_overhangs(
near_points.add(LayerSupportPoint{ near_points.add(LayerSupportPoint{
SupportPoint{ SupportPoint{
Vec3f{unscale<float>(p.x()), unscale<float>(p.y()), part_z}, Vec3f{unscale<float>(p.x()), unscale<float>(p.y()), part_z},
/* head_front_radius */ 0.4f, /* head_front_radius */ config.head_diameter / 2,
SupportPointType::slope SupportPointType::slope
}, },
/* position_on_layer */ p, /* position_on_layer */ p,
@ -278,7 +279,7 @@ void support_island(const LayerPart &part, NearPoints& near_points, float part_z
unscale<float>(sample->point.y()), unscale<float>(sample->point.y()),
part_z part_z
}, },
/* head_front_radius */ 0.4f, /* head_front_radius */ cfg.head_diameter / 2,
SupportPointType::island SupportPointType::island
}, },
/* position_on_layer */ sample->point, /* position_on_layer */ sample->point,
@ -481,12 +482,10 @@ void prepare_supports_for_layer(LayerSupportPoints &supports, float layer_z,
/// </summary> /// </summary>
/// <param name="near_points"></param> /// <param name="near_points"></param>
/// <param name="part"></param> /// <param name="part"></param>
void remove_supports_out_of_part(NearPoints& near_points, const LayerPart &part) { /// <param name="config"></param>
void remove_supports_out_of_part(NearPoints& near_points, const LayerPart &part,
// Must be greater than surface texture and lower than self supporting area const SupportPointGeneratorConfig &config) {
// May be use maximal island distance ExPolygons extend_shape = offset_ex(*part.shape, config.removing_delta, ClipperLib::jtSquare);
float delta = scale_(5.);
ExPolygons extend_shape = offset_ex(*part.shape, delta, ClipperLib::jtSquare);
near_points.remove_out_of(extend_shape); near_points.remove_out_of(extend_shape);
} }
@ -660,7 +659,7 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
assert(layer_id != 0); assert(layer_id != 0);
const LayerParts &prev_layer_parts = layers[layer_id - 1].parts; const LayerParts &prev_layer_parts = layers[layer_id - 1].parts;
NearPoints near_points = create_near_points(prev_layer_parts, part, prev_grids); NearPoints near_points = create_near_points(prev_layer_parts, part, prev_grids);
remove_supports_out_of_part(near_points, part); remove_supports_out_of_part(near_points, part, config);
support_part_overhangs(part, config, near_points, layer.print_z, maximal_radius); support_part_overhangs(part, config, near_points, layer.print_z, maximal_radius);
grids.push_back(std::move(near_points)); grids.push_back(std::move(near_points));
} }

View File

@ -51,6 +51,13 @@ struct SupportPointGeneratorConfig{
// Configuration for sampling island // Configuration for sampling island
SampleConfig island_configuration = SampleConfigFactory::create(head_diameter); SampleConfig island_configuration = SampleConfigFactory::create(head_diameter);
// To be able support same 2d area multipletimes,
// It is neccessary to remove support point form near KDTree structure
// Must be greater than surface texture and lower than self supporting area
// May be use maximal island distance
float removing_delta = scale_(5.);
}; };
struct LayerPart; // forward decl. struct LayerPart; // forward decl.