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,
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);
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");
return svg;
}
@ -176,7 +177,7 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
#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
SupportIslandPoints samples = sample_expath(longest_path, lines, config);
@ -184,7 +185,7 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
#ifdef OPTION_TO_STORE_ISLAND
Points samples_before_align = to_points(samples);
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);
}
#endif // OPTION_TO_STORE_ISLAND
@ -193,13 +194,17 @@ SupportIslandPoints SampleIslandUtils::uniform_cover_island(
SampleIslandUtils::align_samples(samples, island, config);
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()) {
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config.head_radius / 10);
draw(svg, samples, config.head_radius);
SVG svg = draw_island(path, island, simplified_island);
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;
align_moves.reserve(samples.size());
for (size_t i = 0; i < samples.size(); ++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
return samples;
@ -1130,7 +1135,6 @@ SupportIslandPoints SampleIslandUtils::sample_expath(
coord_t support_in = config.max_distance + already_supported;
center_starts.emplace_back(position->neighbor, support_in, start_path);
} else {
assert(position.has_value());
done.insert(start_node);
coord_t support_in = config.minimal_distance_from_outline;
center_starts.emplace_back(neighbor, support_in);
@ -1263,16 +1267,15 @@ std::optional<VoronoiGraph::Position> SampleIslandUtils::sample_center(
std::set<const VoronoiGraph::Node *> &done,
SupportIslandPoints & results,
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
CenterStart start(nullptr, {}, {});
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.
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
@ -1489,16 +1492,16 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
// 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 =
[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
// Prepare data for field outline,
// when field transit into tiny part of island
auto add_wide_tiny_change_only = [&wide_tiny_changes, &lines, &tiny_done]
(const VoronoiGraph::Position &position){
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();
// function to add sorted change from wide to tiny
// 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];
if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) {
// 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
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_done.insert(source_node);
return true;
@ -1543,6 +1553,7 @@ SampleIslandUtils::Field SampleIslandUtils::create_field(
std::set<const VoronoiGraph::Node*> done;
done.insert(wide_tiny_neighbor->node);
// prev node , node
using ProcessItem = std::pair<const VoronoiGraph::Node *, const VoronoiGraph::Node *>;
// 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))
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) {
next_node = neighbor.node;
} else {
@ -1993,10 +2015,10 @@ void SampleIslandUtils::draw(SVG & svg,
void SampleIslandUtils::draw(SVG & svg,
const SupportIslandPoints &supportIslandPoints,
double size,
const char * color,
coord_t radius,
bool write_type)
{
const char *color = nullptr;
for (const auto &p : supportIslandPoints) {
switch (p->type) {
case SupportIslandPoint::Type::center_line1:
@ -2016,11 +2038,11 @@ void SampleIslandUtils::draw(SVG & svg,
case SupportIslandPoint::Type::two_points:
default: color = "black";
}
svg.draw(p->point, color, size);
svg.draw(p->point, color, radius);
if (write_type && p->type != SupportIslandPoint::Type::undefined) {
auto type_name = SupportIslandPoint::to_string(p->type);
Point start = p->point + Point(size, 0.);
svg.draw_text(start, std::string(type_name).c_str(), color);
Point start = p->point + Point(radius, 0);
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="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="is_continous">Already place sample on path</param>
/// <returns>Wide neighbor, start of field when exists</returns>
static std::optional<VoronoiGraph::Position> sample_center(
CenterStarts & new_starts,
std::set<const VoronoiGraph::Node *> &done,
SupportIslandPoints & results,
const Lines & lines,
const SampleConfig & config);
const SampleConfig & config,
bool is_continous = false
);
private:
/// <summary>
@ -390,8 +393,7 @@ public :
static void draw(SVG & svg,
const SupportIslandPoints &supportIslandPoints,
double size,
const char *color = "lightgreen",
coord_t radius,
bool write_type = true);
};

View File

@ -1301,20 +1301,45 @@ double VoronoiGraphUtils::outline_angle(const VoronoiGraph::Node::Neighbor &neig
void VoronoiGraphUtils::draw(SVG & svg,
const VoronoiGraph &graph,
const Lines & lines,
coord_t width,
const SampleConfig &config,
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){
if (pointer_caption) {
std::stringstream ss;
ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr);
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) {
Point p(key->x(), key->y());
svg.draw(p, "lightgray", width);
@ -1328,10 +1353,13 @@ void VoronoiGraphUtils::draw(SVG & svg,
Point(0., 2e6));
print_address(p, "neighbor ptr ", (void *) &n, "gray");
if (is_second) continue;
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);
const char *color = get_color(n);
if (pointer_caption) {
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(), color, 6);
}
draw(svg, *n.edge, lines, color, width);
}
}
}
@ -1358,7 +1386,8 @@ void VoronoiGraphUtils::draw(SVG & svg,
const VoronoiGraph::Nodes &path,
coord_t width,
const char * color,
bool finish)
bool finish,
bool caption)
{
const VoronoiGraph::Node *prev_node = (finish) ? path.back() : nullptr;
int index = 0;
@ -1372,9 +1401,10 @@ void VoronoiGraphUtils::draw(SVG & svg,
Point from = to_point(prev_node->vertex);
Point to = to_point(node->vertex);
svg.draw(Line(from, to), color, width);
svg.draw_text(from, std::to_string(index - 1).c_str(), color);
svg.draw_text(to, std::to_string(index).c_str(), color);
if (caption) {
svg.draw_text(from, std::to_string(index - 1).c_str(), color, 6);
svg.draw_text(to, std::to_string(index).c_str(), color, 6);
}
prev_node = node;
}
}

View File

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

View File

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

View File

@ -51,6 +51,13 @@ struct SupportPointGeneratorConfig{
// Configuration for sampling island
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.