Replaced unnecessary double storing of edges in a graph for the multi-material segmentation by storing indices into a shared array of arcs.

This commit is contained in:
Lukáš Hejl 2021-07-28 14:42:27 +02:00
parent 216589fdb1
commit 37f5f12f52

View file

@ -517,7 +517,6 @@ struct MMU_Graph
size_t to_idx; size_t to_idx;
int color; int color;
ARC_TYPE type; ARC_TYPE type;
bool used{false};
bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); } bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); }
bool operator!=(const Arc &rhs) const { return !operator==(rhs); } bool operator!=(const Arc &rhs) const { return !operator==(rhs); }
@ -525,15 +524,16 @@ struct MMU_Graph
struct Node struct Node
{ {
Point point; Point point;
std::list<MMU_Graph::Arc> neighbours; std::list<size_t> arc_idxs;
void remove_edge(const size_t to_idx) void remove_edge(const size_t to_idx, MMU_Graph &graph)
{ {
for (auto arc_it = this->neighbours.begin(); arc_it != this->neighbours.end(); ++arc_it) { for (auto arc_it = this->arc_idxs.begin(); arc_it != this->arc_idxs.end(); ++arc_it) {
if (arc_it->to_idx == to_idx) { MMU_Graph::Arc &arc = graph.arcs[*arc_it];
assert(arc_it->type != ARC_TYPE::BORDER); if (arc.to_idx == to_idx) {
this->neighbours.erase(arc_it); assert(arc.type != ARC_TYPE::BORDER);
this->arc_idxs.erase(arc_it);
break; break;
} }
} }
@ -549,8 +549,8 @@ struct MMU_Graph
void remove_edge(const size_t from_idx, const size_t to_idx) void remove_edge(const size_t from_idx, const size_t to_idx)
{ {
nodes[from_idx].remove_edge(to_idx); nodes[from_idx].remove_edge(to_idx, *this);
nodes[to_idx].remove_edge(from_idx); nodes[to_idx].remove_edge(from_idx, *this);
} }
[[nodiscard]] size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; } [[nodiscard]] size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; }
@ -558,27 +558,27 @@ struct MMU_Graph
void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER) void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER)
{ {
// Don't append duplicate edges between the same nodes. // Don't append duplicate edges between the same nodes.
for (const MMU_Graph::Arc &arc : this->nodes[from_idx].neighbours) for (const size_t &arc_idx : this->nodes[from_idx].arc_idxs)
if (arc.to_idx == to_idx) if (arcs[arc_idx].to_idx == to_idx)
return; return;
for (const MMU_Graph::Arc &arc : this->nodes[to_idx].neighbours) for (const size_t &arc_idx : this->nodes[to_idx].arc_idxs)
if (arc.to_idx == to_idx) if (arcs[arc_idx].to_idx == to_idx)
return; return;
this->nodes[from_idx].neighbours.push_back({from_idx, to_idx, color, type}); this->nodes[from_idx].arc_idxs.push_back(this->arcs.size());
this->arcs.push_back({from_idx, to_idx, color, type}); this->arcs.push_back({from_idx, to_idx, color, type});
// Always insert only one directed arc for the input polygons. // 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. // Two directed arcs in both directions are inserted if arcs aren't between points of the input polygons.
if (type == ARC_TYPE::NON_BORDER) { if (type == ARC_TYPE::NON_BORDER) {
this->nodes[to_idx].neighbours.push_back({to_idx, from_idx, color, type}); this->nodes[to_idx].arc_idxs.push_back(this->arcs.size());
this->arcs.push_back({to_idx, from_idx, color, type}); this->arcs.push_back({to_idx, from_idx, color, type});
} }
} }
// It assumes that between points of the input polygons is always only one directed arc, // 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. // with the same direction as lines of the input polygon.
MMU_Graph::Arc get_border_arc(size_t idx) { [[nodiscard]] MMU_Graph::Arc get_border_arc(size_t idx) const {
assert(idx < this->all_border_points); assert(idx < this->all_border_points);
return this->arcs[idx]; return this->arcs[idx];
} }
@ -591,7 +591,7 @@ struct MMU_Graph
for (const MMU_Graph::Node &node : this->nodes) { for (const MMU_Graph::Node &node : this->nodes) {
size_t node_idx = &node - &this->nodes.front(); size_t node_idx = &node - &this->nodes.front();
// Skip nodes that represent points of input polygons. // Skip nodes that represent points of input polygons.
if (node.neighbours.size() == 1 && node_idx >= this->all_border_points) if (node.arc_idxs.size() == 1 && node_idx >= this->all_border_points)
update_queue.emplace(&node - &this->nodes.front()); update_queue.emplace(&node - &this->nodes.front());
} }
@ -599,14 +599,14 @@ struct MMU_Graph
size_t node_from_idx = update_queue.front(); size_t node_from_idx = update_queue.front();
MMU_Graph::Node &node_from = this->nodes[update_queue.front()]; MMU_Graph::Node &node_from = this->nodes[update_queue.front()];
update_queue.pop(); update_queue.pop();
if (node_from.neighbours.empty()) if (node_from.arc_idxs.empty())
continue; continue;
assert(node_from.neighbours.size() == 1); assert(node_from.arc_idxs.size() == 1);
size_t node_to_idx = node_from.neighbours.front().to_idx; size_t node_to_idx = arcs[node_from.arc_idxs.front()].to_idx;
MMU_Graph::Node &node_to = this->nodes[node_to_idx]; MMU_Graph::Node &node_to = this->nodes[node_to_idx];
this->remove_edge(node_from_idx, node_to_idx); this->remove_edge(node_from_idx, node_to_idx);
if (node_to.neighbours.size() == 1 && node_to_idx >= this->all_border_points) if (node_to.arc_idxs.size() == 1 && node_to_idx >= this->all_border_points)
update_queue.emplace(node_to_idx); update_queue.emplace(node_to_idx);
} }
} }
@ -707,6 +707,35 @@ struct MMU_Graph
} }
} }
} }
void garbage_collect()
{
std::vector<int> nodes_map(this->nodes.size(), -1);
int nodes_count = 0;
size_t arcs_count = 0;
for (const MMU_Graph::Node &node : this->nodes)
if (size_t node_idx = &node - &this->nodes.front(); !node.arc_idxs.empty()) {
nodes_map[node_idx] = nodes_count++;
arcs_count += node.arc_idxs.size();
}
std::vector<MMU_Graph::Node> new_nodes;
std::vector<MMU_Graph::Arc> new_arcs;
new_nodes.reserve(nodes_count);
new_arcs.reserve(arcs_count);
for (const MMU_Graph::Node &node : this->nodes)
if (size_t node_idx = &node - &this->nodes.front(); nodes_map[node_idx] >= 0) {
new_nodes.push_back({node.point});
for (const size_t &arc_idx : node.arc_idxs) {
const Arc &arc = this->arcs[arc_idx];
new_nodes.back().arc_idxs.emplace_back(new_arcs.size());
new_arcs.push_back({size_t(nodes_map[arc.from_idx]), size_t(nodes_map[arc.to_idx]), arc.color, arc.type});
}
}
this->nodes = std::move(new_nodes);
this->arcs = std::move(new_arcs);
}
}; };
static inline void mark_processed(const voronoi_diagram<double>::const_edge_iterator &edge_iterator) static inline void mark_processed(const voronoi_diagram<double>::const_edge_iterator &edge_iterator)
@ -995,13 +1024,15 @@ static inline Polygon to_polygon(const Lines &lines)
// It iterates through all nodes on the border between two different colors, and from this point, // It iterates through all nodes on the border between two different colors, and from this point,
// start selection always left most edges for every node to construct CCW polygons. // start selection always left most edges for every node to construct CCW polygons.
// Assumes that graph is planar (without self-intersection edges) // Assumes that graph is planar (without self-intersection edges)
static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Graph &graph) static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(const MMU_Graph &graph)
{ {
std::vector<bool> used_arcs(graph.arcs.size(), false);
// When there is no next arc, then is returned original_arc or edge with is marked as used // When there is no next arc, then is returned original_arc or edge with is marked as used
auto get_next = [&graph](const Line &process_line, MMU_Graph::Arc &original_arc) -> MMU_Graph::Arc & { auto get_next = [&graph, &used_arcs](const Line &process_line, const MMU_Graph::Arc &original_arc) -> const MMU_Graph::Arc & {
std::vector<std::pair<MMU_Graph::Arc *, double>> sorted_arcs; std::vector<std::pair<const MMU_Graph::Arc *, double>> sorted_arcs;
for (MMU_Graph::Arc &arc : graph.nodes[original_arc.to_idx].neighbours) { for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
if (graph.nodes[arc.to_idx].point == process_line.a || arc.used) const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
if (graph.nodes[arc.to_idx].point == process_line.a || used_arcs[arc_idx])
continue; continue;
assert(original_arc.to_idx == arc.from_idx); assert(original_arc.to_idx == arc.from_idx);
@ -1016,11 +1047,11 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
} }
std::sort(sorted_arcs.begin(), sorted_arcs.end(), std::sort(sorted_arcs.begin(), sorted_arcs.end(),
[](std::pair<MMU_Graph::Arc *, double> &l, std::pair<MMU_Graph::Arc *, double> &r) -> bool { return l.second < r.second; }); [](std::pair<const MMU_Graph::Arc *, double> &l, std::pair<const MMU_Graph::Arc *, double> &r) -> bool { return l.second < r.second; });
// Try to return left most edge witch is unused // Try to return left most edge witch is unused
for (auto &sorted_arc : sorted_arcs) for (auto &sorted_arc : sorted_arcs)
if (!sorted_arc.first->used) if (size_t arc_idx = sorted_arc.first - &graph.arcs.front(); !used_arcs[arc_idx])
return *sorted_arc.first; return *sorted_arc.first;
if (sorted_arcs.empty()) if (sorted_arcs.empty())
@ -1029,40 +1060,38 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
return *(sorted_arcs.front().first); return *(sorted_arcs.front().first);
}; };
auto all_arc_used = [](const MMU_Graph::Node &node) -> bool { auto all_arc_used = [&used_arcs](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; }); return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; });
}; };
std::vector<std::pair<Polygon, size_t>> polygons_segments; std::vector<std::pair<Polygon, size_t>> polygons_segments;
for (MMU_Graph::Node &node : graph.nodes)
for (MMU_Graph::Arc &arc : node.neighbours)
arc.used = false;
for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) { for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) {
MMU_Graph::Node &node = graph.nodes[node_idx]; const MMU_Graph::Node &node = graph.nodes[node_idx];
for (const size_t &arc_idx : node.arc_idxs) {
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || used_arcs[arc_idx])continue;
for (MMU_Graph::Arc &arc : node.neighbours) {
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || arc.used)
continue;
Line process_line(node.point, graph.nodes[arc.to_idx].point); Line process_line(node.point, graph.nodes[arc.to_idx].point);
arc.used = true; used_arcs[arc_idx] = true;
Lines face_lines; Lines face_lines;
face_lines.emplace_back(process_line); face_lines.emplace_back(process_line);
Point start_p = process_line.a; Point start_p = process_line.a;
Line p_vec = process_line; Line p_vec = process_line;
MMU_Graph::Arc *p_arc = &arc; const MMU_Graph::Arc *p_arc = &arc;
do { do {
MMU_Graph::Arc &next = get_next(p_vec, *p_arc); const 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)); size_t next_arc_idx = &next - &graph.arcs.front();
if (next.used) face_lines.emplace_back(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
if (used_arcs[next_arc_idx])
break; break;
next.used = true; used_arcs[next_arc_idx] = true;
p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point); p_vec = Line(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
p_arc = &next; p_arc = &next;
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx])); } 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); Polygon poly = to_polygon(face_lines);
@ -1076,20 +1105,19 @@ static std::vector<std::pair<Polygon, size_t>> extract_colored_segments(MMU_Grap
// Used in remove_multiple_edges_in_vertices() // Used in remove_multiple_edges_in_vertices()
// Returns length of edge with is connected to contour. To this length is include other edges with follows it if they are almost straight (with the // Returns length of edge with is connected to contour. To this length is include other edges with follows it if they are almost straight (with the
// tolerance of 15) And also if node between two subsequent edges is connected only to these two edges. // tolerance of 15) And also if node between two subsequent edges is connected only to these two edges.
static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU_Graph::Arc &start_edge) static inline double compute_edge_length(const MMU_Graph &graph, const size_t start_idx, const size_t &start_arc_idx)
{ {
for (MMU_Graph::Node &node : graph.nodes) assert(start_arc_idx < graph.arcs.size());
for (MMU_Graph::Arc &arc : node.neighbours) std::vector<bool> used_arcs(graph.arcs.size(), false);
arc.used = false;
start_edge.used = true; used_arcs[start_arc_idx] = true;
MMU_Graph::Arc *arc = &start_edge; const MMU_Graph::Arc *arc = &graph.arcs[start_arc_idx];
size_t idx = start_idx; size_t idx = start_idx;
double line_total_length = Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).cast<double>().norm();;
while (graph.nodes[arc->to_idx].neighbours.size() == 2) { while (graph.nodes[arc->to_idx].arc_idxs.size() == 2) {
bool found = false; bool found = false;
for (MMU_Graph::Arc &arc_n : graph.nodes[arc->to_idx].neighbours) { for (const size_t &arc_idx : graph.nodes[arc->to_idx].arc_idxs) {
if (arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !arc_n.used && arc_n.to_idx != idx) { if (const MMU_Graph::Arc &arc_n = graph.arcs[arc_idx]; arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !used_arcs[arc_idx] && arc_n.to_idx != idx) {
Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point); Line first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point);
Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point); Line second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point);
@ -1107,8 +1135,8 @@ static inline double compute_edge_length(MMU_Graph &graph, size_t start_idx, MMU
idx = arc->to_idx; idx = arc->to_idx;
arc = &arc_n; arc = &arc_n;
line_total_length += Line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point).length(); line_total_length += (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).cast<double>().norm();
arc_n.used = true; used_arcs[arc_idx] = true;
found = true; found = true;
break; break;
} }
@ -1131,11 +1159,12 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]); size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]);
Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point); Line seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point);
if (graph.nodes[first_idx].neighbours.size() >= 3) { if (graph.nodes[first_idx].arc_idxs.size() >= 3) {
std::vector<std::pair<MMU_Graph::Arc *, double>> arc_to_check; std::vector<std::pair<MMU_Graph::Arc *, double>> arc_to_check;
for (MMU_Graph::Arc &n_arc : graph.nodes[first_idx].neighbours) { for (const size_t &arc_idx : graph.nodes[first_idx].arc_idxs) {
MMU_Graph::Arc &n_arc = graph.arcs[arc_idx];
if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) { if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) {
double total_len = compute_edge_length(graph, first_idx, n_arc); double total_len = compute_edge_length(graph, first_idx, arc_idx);
arc_to_check.emplace_back(&n_arc, total_len); arc_to_check.emplace_back(&n_arc, total_len);
} }
} }
@ -1659,7 +1688,8 @@ static void export_graph_to_svg(const std::string &path, const MMU_Graph &graph,
bbox.offset(scale_(1.)); bbox.offset(scale_(1.));
::Slic3r::SVG svg(path.c_str(), bbox); ::Slic3r::SVG svg(path.c_str(), bbox);
for (const MMU_Graph::Node &node : graph.nodes) for (const MMU_Graph::Node &node : graph.nodes)
for (const MMU_Graph::Arc &arc : node.neighbours) { for (const size_t &arc_idx : node.arc_idxs) {
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
Line arc_line(node.point, graph.nodes[arc.to_idx].point); 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())) 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); svg.draw(arc_line, colors[arc.color], stroke_width);