Fixed a possible overflowing of a painted area to an unpainted area in multi-material segmentation.
It was reworked graph generation for multi-material segmentation. Now only oriented arcs in one direction are added to the graph for input polygons. This direction matches the direction of the lines in the input polygons.
This commit is contained in:
parent
1d4e7f5577
commit
7a60f1793c
1 changed files with 119 additions and 37 deletions
|
@ -38,6 +38,9 @@ struct segment_traits<Slic3r::ColoredLine> {
|
|||
};
|
||||
}
|
||||
|
||||
//#define MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
//#define MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Assumes that is at most same projected_l length or below than projection_l
|
||||
|
@ -556,21 +559,34 @@ struct MMU_Graph
|
|||
return;
|
||||
|
||||
this->nodes[from_idx].neighbours.push_back({from_idx, to_idx, color, type});
|
||||
this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type});
|
||||
this->arcs.push_back({from_idx, to_idx, color, type});
|
||||
|
||||
// Always insert only one directed arc for the input polygons.
|
||||
// Two directed arcs in both directions are inserted if arcs aren't between points of the input polygons.
|
||||
if (type == ARC_TYPE::NON_BORDER) {
|
||||
this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type});
|
||||
this->arcs.push_back({to_idx, from_idx, color, type});
|
||||
}
|
||||
}
|
||||
|
||||
// Ignoring arcs in the opposite direction
|
||||
MMU_Graph::Arc get_arc(size_t idx) { return this->arcs[idx * 2]; }
|
||||
// It assumes that between points of the input polygons is always only one directed arc,
|
||||
// with the same direction as lines of the input polygon.
|
||||
MMU_Graph::Arc get_border_arc(size_t idx) {
|
||||
assert(idx < this->all_border_points);
|
||||
return this->arcs[idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t nodes_count() const { return this->nodes.size(); }
|
||||
|
||||
void remove_nodes_with_one_arc()
|
||||
{
|
||||
std::queue<size_t> update_queue;
|
||||
for (const MMU_Graph::Node &node : this->nodes)
|
||||
if (node.neighbours.size() == 1) update_queue.emplace(&node - &this->nodes.front());
|
||||
for (const MMU_Graph::Node &node : this->nodes) {
|
||||
size_t node_idx = &node - &this->nodes.front();
|
||||
// Skip nodes that represent points of input polygons.
|
||||
if (node.neighbours.size() == 1 && node_idx >= this->all_border_points)
|
||||
update_queue.emplace(&node - &this->nodes.front());
|
||||
}
|
||||
|
||||
while (!update_queue.empty()) {
|
||||
size_t node_from_idx = update_queue.front();
|
||||
|
@ -583,7 +599,7 @@ struct MMU_Graph
|
|||
size_t node_to_idx = node_from.neighbours.front().to_idx;
|
||||
MMU_Graph::Node &node_to = this->nodes[node_to_idx];
|
||||
this->remove_edge(node_from_idx, node_to_idx);
|
||||
if (node_to.neighbours.size() == 1)
|
||||
if (node_to.neighbours.size() == 1 && node_to_idx >= this->all_border_points)
|
||||
update_queue.emplace(node_to_idx);
|
||||
}
|
||||
}
|
||||
|
@ -660,17 +676,17 @@ struct MMU_Graph
|
|||
vertex.color(-1);
|
||||
Point vertex_point = mk_point(vertex);
|
||||
|
||||
const Point &first_point = this->nodes[this->get_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Point &second_point = this->nodes[this->get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
const Point &first_point = this->nodes[this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Point &second_point = this->nodes[this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
|
||||
if (vertex_equal_to_point(&vertex, first_point)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_arc(vertex.incident_edge()->cell()->source_index()).from_idx);
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx);
|
||||
} else if (vertex_equal_to_point(&vertex, second_point)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
|
||||
} else if (bbox.contains(vertex_point)) {
|
||||
if (auto [contour_pt, c_dist_sqr] = closest_contour_point.find(vertex_point); contour_pt != nullptr && c_dist_sqr < 3 * SCALED_EPSILON) {
|
||||
vertex.color(this->get_global_index(contour_pt->m_contour_idx, contour_pt->m_point_idx));
|
||||
|
@ -825,7 +841,7 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
Point contour_intersection;
|
||||
|
||||
if (line_intersection_with_epsilon(contour_line.line, edge_line, &contour_intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index());
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->cell()->source_index());
|
||||
const size_t from_idx = (edge_it->vertex1() != nullptr) ? edge_it->vertex1()->color() : edge_it->vertex0()->color();
|
||||
size_t to_idx = ((contour_line.line.a - contour_intersection).cast<double>().squaredNorm() <
|
||||
(contour_line.line.b - contour_intersection).cast<double>().squaredNorm()) ?
|
||||
|
@ -859,12 +875,12 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1())) {
|
||||
Line contour_line_twin = lines_colored[edge_it->twin()->cell()->source_index()].line;
|
||||
if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->twin()->cell()->source_index());
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->twin()->cell()->source_index());
|
||||
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx :
|
||||
graph_arc.to_idx;
|
||||
graph.append_edge(edge_it->vertex1()->color(), to_idx_l);
|
||||
} else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_arc(edge_it->cell()->source_index());
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->cell()->source_index());
|
||||
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx;
|
||||
graph.append_edge(edge_it->vertex1()->color(), to_idx_l);
|
||||
}
|
||||
|
@ -912,32 +928,30 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
|
|||
Line second_part(intersection, real_v1);
|
||||
|
||||
if (!has_same_color(contour_line_prev, colored_line)) {
|
||||
if (points_inside(contour_line_prev.line, contour_line, first_part.b)) {
|
||||
graph.append_edge(edge_it->vertex0()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
if (points_inside(contour_line_prev.line, contour_line, second_part.b)) {
|
||||
graph.append_edge(edge_it->vertex1()->color(), graph.get_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
if (points_inside(contour_line_prev.line, contour_line, first_part.b))
|
||||
graph.append_edge(edge_it->vertex0()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
|
||||
if (points_inside(contour_line_prev.line, contour_line, second_part.b))
|
||||
graph.append_edge(edge_it->vertex1()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
} else {
|
||||
const size_t int_point_idx = graph.get_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const size_t int_point_idx = graph.get_border_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const Point int_point = graph.nodes[int_point_idx].point;
|
||||
|
||||
const Line first_part(int_point, real_v0);
|
||||
const Line second_part(int_point, real_v1);
|
||||
|
||||
if (!has_same_color(contour_line_next, colored_line)) {
|
||||
if (points_inside(contour_line, contour_line_next.line, first_part.b)) {
|
||||
if (points_inside(contour_line, contour_line_next.line, first_part.b))
|
||||
graph.append_edge(edge_it->vertex0()->color(), int_point_idx);
|
||||
}
|
||||
if (points_inside(contour_line, contour_line_next.line, second_part.b)) {
|
||||
|
||||
if (points_inside(contour_line, contour_line_next.line, second_part.b))
|
||||
graph.append_edge(edge_it->vertex1()->color(), int_point_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) {
|
||||
// Skip second half-edge and processed edges
|
||||
|
@ -1008,6 +1022,10 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
|
|||
return *(sorted_arcs.front().first);
|
||||
};
|
||||
|
||||
auto all_arc_used = [](const MMU_Graph::Node &node) -> bool {
|
||||
return std::all_of(node.neighbours.cbegin(), node.neighbours.cend(), [](const MMU_Graph::Arc &arc) -> bool { return arc.used; });
|
||||
};
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> polygons_segments;
|
||||
for (MMU_Graph::Node &node : graph.nodes)
|
||||
for (MMU_Graph::Arc &arc : node.neighbours)
|
||||
|
@ -1017,7 +1035,8 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
|
|||
MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||
|
||||
for (MMU_Graph::Arc &arc : node.neighbours) {
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used) continue;
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used)
|
||||
continue;
|
||||
|
||||
Line process_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
arc.used = true;
|
||||
|
@ -1031,12 +1050,13 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
|
|||
do {
|
||||
MMU_Graph::Arc &next = get_next(p_vec, *p_arc);
|
||||
face_lines.emplace_back(Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point));
|
||||
if (next.used) break;
|
||||
if (next.used)
|
||||
break;
|
||||
|
||||
next.used = true;
|
||||
p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
|
||||
p_arc = &next;
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p);
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
||||
|
||||
Polygon poly = to_polygon(face_lines);
|
||||
if (poly.is_counter_clockwise() && poly.is_valid())
|
||||
|
@ -1482,14 +1502,14 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
|||
// color_idx == 0 means "don't know" extruder aka the underlying extruder.
|
||||
// As this region may split existing regions, we collect statistics over all regions for color_idx == 0.
|
||||
color_idx == 0 || config.perimeter_extruder == int(color_idx)) {
|
||||
out.extrusion_width = std::max<float>(out.extrusion_width, config.perimeter_extrusion_width);
|
||||
out.top_solid_layers = std::max<float>(out.top_solid_layers, config.top_solid_layers);
|
||||
out.bottom_solid_layers = std::max<float>(out.bottom_solid_layers, config.bottom_solid_layers);
|
||||
out.extrusion_width = std::max<float>(out.extrusion_width, float(config.perimeter_extrusion_width));
|
||||
out.top_solid_layers = std::max<int>(out.top_solid_layers, config.top_solid_layers);
|
||||
out.bottom_solid_layers = std::max<int>(out.bottom_solid_layers, config.bottom_solid_layers);
|
||||
out.small_region_threshold = config.gap_fill_enabled.value && config.gap_fill_speed.value > 0 ?
|
||||
// Gap fill enabled. Enable a single line of 1/2 extrusion width.
|
||||
0.5 * config.perimeter_extrusion_width :
|
||||
0.5f * float(config.perimeter_extrusion_width) :
|
||||
// Gap fill disabled. Enable two lines slightly overlapping.
|
||||
config.perimeter_extrusion_width + 0.7f * Flow::rounded_rectangle_extrusion_spacing(config.perimeter_extrusion_width, layer.height);
|
||||
float(config.perimeter_extrusion_width) + 0.7f * Flow::rounded_rectangle_extrusion_spacing(float(config.perimeter_extrusion_width), float(layer.height));
|
||||
out.small_region_threshold = scaled<float>(out.small_region_threshold * 0.5f);
|
||||
++ out.num_regions;
|
||||
}
|
||||
|
@ -1603,6 +1623,45 @@ static std::vector<std::vector<std::pair<ExPolygon, size_t>>> merge_segmented_la
|
|||
return segmented_regions_merged;
|
||||
}
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
static void export_regions_to_svg(const std::string &path, const std::vector<std::pair<ExPolygon, size_t>> ®ions, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(lslices);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
|
||||
svg.draw_outline(lslices, "green", "lime", stroke_width);
|
||||
for (const std::pair<ExPolygon, size_t> ®ion : regions) {
|
||||
int region_color = region.second;
|
||||
if (region_color >= 0 && region_color < int(colors.size()))
|
||||
svg.draw(region.first, colors[region_color]);
|
||||
else
|
||||
svg.draw(region.first, "black");
|
||||
}
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
static void export_graph_to_svg(const std::string &path, const MMU_Graph &graph, const ExPolygons &lslices)
|
||||
{
|
||||
const std::vector<std::string> colors = {"blue", "cyan", "red", "orange", "magenta", "pink", "purple", "green", "yellow"};
|
||||
coordf_t stroke_width = scale_(0.05);
|
||||
BoundingBox bbox = get_extents(lslices);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(path.c_str(), bbox);
|
||||
for (const MMU_Graph::Node &node : graph.nodes)
|
||||
for (const MMU_Graph::Arc &arc : node.neighbours) {
|
||||
Line arc_line(node.point, graph.nodes[arc.to_idx].point);
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::BORDER && arc.color >= 0 && arc.color < int(colors.size()))
|
||||
svg.draw(arc_line, colors[arc.color], stroke_width);
|
||||
else
|
||||
svg.draw(arc_line, "black", stroke_width);
|
||||
}
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions(print_object.layers().size());
|
||||
|
@ -1744,9 +1803,24 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
||||
remove_multiple_edges_in_vertices(graph, color_poly);
|
||||
graph.remove_nodes_with_one_arc();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_graph_to_svg(debug_out_path("mm-graph-final-%d-%d.svg", layer_idx, iRun++), graph, input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_GRAPH
|
||||
|
||||
std::vector<std::pair<Polygon, size_t>> segmentation = extract_colored_segments(graph);
|
||||
for (std::pair<Polygon, size_t> ®ion : segmentation)
|
||||
segmented_regions[layer_idx].emplace_back(std::move(region));
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_regions_to_svg(debug_out_path("mm-regions-sides-%d-%d.svg", layer_idx, iRun++), segmented_regions[layer_idx], input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
|
@ -1765,6 +1839,14 @@ std::vector<std::vector<std::pair<ExPolygon, size_t>>> multi_material_segmentati
|
|||
std::vector<std::vector<std::pair<ExPolygon, size_t>>> segmented_regions_merged = merge_segmented_layers(segmented_regions, std::move(top_and_bottom_layers), throw_on_cancel_callback);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
#ifdef MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
{
|
||||
static int iRun = 0;
|
||||
for (size_t layer_idx = 0; layer_idx < print_object.layers().size(); ++layer_idx)
|
||||
export_regions_to_svg(debug_out_path("mm-regions-merged-%d-%d.svg", layer_idx, iRun++), segmented_regions_merged[layer_idx], input_expolygons[layer_idx]);
|
||||
}
|
||||
#endif // MMU_SEGMENTATION_DEBUG_REGIONS
|
||||
|
||||
return segmented_regions_merged;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue