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:
parent
216589fdb1
commit
37f5f12f52
1 changed files with 95 additions and 65 deletions
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue