GraphvizLayout.cpp
Go to the documentation of this file.
1#include "GraphvizLayout.h"
2
3#include <vector>
4
5#include <SimoxUtility/algorithm/string/string_tools.h>
6
8
9#include <graphviz/cgraph.h>
10
11namespace armarx
12{
13
14 namespace
15 {
16
17 char*
18 gv_str(const char* c)
19 {
20 return const_cast<char*>(c);
21 }
22
23 char*
24 gv_str(std::string const& s)
25 {
26 return const_cast<char*>(s.c_str());
27 }
28
29 char GRAPHVIZ_ATTR_LABEL[] = "label";
30 char GRAPHVIZ_ATTR_POS[] = "pos";
31 char GRAPHVIZ_ATTR_WIDTH[] = "width";
32 char GRAPHVIZ_ATTR_HEIGHT[] = "height";
33 char GRAPHVIZ_ATTR_LABEL_POS[] = "lp";
34 } // namespace
35
37 {
38 context = gvContext();
39 graph = agopen(gv_str("Graph"), Agdirected, nullptr);
40
41
42 // Setup properties
43 agattr(
44 graph,
45 AGRAPH,
46 (char*)"dpi",
47 (char*)"72"); // needs to be 72 because graphviz's bounding box is always calculated with 72dpi
48 // Bounding box of drawing
49 // How is this used and specified? In pixels?
50 // Do we need it?
51 // agattr(graph, AGRAPH, (char*)"bb", (char*)"0,0,1210,744");
52 // Maximum drawing size in inches
53 // Why would we set that?
54 //agattr(graph, AGRAPH, (char*)"size", (char*)"5,5");
55 agattr(graph, AGRAPH, (char*)"overlap", (char*)"prism");
56 agattr(graph, AGRAPH, (char*)"splines", (char*)"true");
57 agattr(graph, AGRAPH, (char*)"pad", (char*)"0.2");
58 agattr(graph, AGRAPH, (char*)"nodesep", (char*)"0.4");
59 agattr(graph, AGRAPH, (char*)"sep", (char*)"1");
60 agattr(graph, AGRAPH, (char*)"overlap_shrink", (char*)"true");
61 agattr(graph, AGRAPH, (char*)"rankdir", (char*)"LR");
62 agattr(graph, AGRAPH, (char*)"ratio", (char*)"compress");
63
64 agattr(graph, AGNODE, (char*)"margin", (char*)"0,0");
65 agattr(graph, AGNODE, (char*)"fontsize", (char*)"28");
66
67 //Set default attributes for future edges
68 agattr(graph, AGEDGE, const_cast<char*>("label"), const_cast<char*>(""));
69 agattr(graph, AGEDGE, const_cast<char*>("fontsize"), const_cast<char*>("28"));
70 }
71
73 {
74 if (graph)
75 {
76 agclose(graph);
77 }
78 if (context)
79 {
80 gvFinalize(context);
81 gvFreeContext(context);
82 }
83 }
84
85 void
86 GraphvizLayout::addNode(int id, const std::string& label)
87 {
88 node_t* node = agnode(graph, gv_str(label), TRUE);
89 agset(node, GRAPHVIZ_ATTR_LABEL, gv_str(label));
90 id2node.emplace(id, node);
91 }
92
93 void
94 GraphvizLayout::addEdge(int sourceID, int targetID, const std::string& label)
95 {
96 node_t* source = id2node.at(sourceID);
97 node_t* target = id2node.at(targetID);
98 edge_t* edge = agedge(graph, source, target, gv_str("edge"), TRUE);
99 if (label.size() > 0)
100 {
101 agset(edge, GRAPHVIZ_ATTR_LABEL, gv_str(label));
102 }
103 id2edge.emplace(std::make_pair(sourceID, targetID), edge);
104 }
105
107 GraphvizLayout::finish(std::string const& savePNG)
108 {
109 gvLayout(context, graph, "dot");
110 attach_attrs(graph);
111 if (!savePNG.empty())
112 {
113 gvRenderFilename(context, graph, "png", gv_str(savePNG));
114 }
115 gvFreeLayout(context, graph);
116
117 GraphvizLayoutedGraph layouted;
118
119 for (auto& pair : id2node)
120 {
121 int id = pair.first;
122 node_t* node = pair.second;
123
124 std::string labelString = agget(node, GRAPHVIZ_ATTR_LABEL);
125 std::string posString = agget(node, GRAPHVIZ_ATTR_POS);
126 std::string widthString = agget(node, GRAPHVIZ_ATTR_WIDTH);
127 std::string heightString = agget(node, GRAPHVIZ_ATTR_HEIGHT);
128
129 std::vector<std::string> splitPos = simox::alg::split(posString, ",");
130 if (splitPos.size() != 2)
131 {
132 continue;
133 }
134
135 float posX = std::stof(splitPos[0]);
136 float posY = std::stof(splitPos[1]);
137 float width = std::stof(widthString);
138 float height = std::stof(heightString);
139 // DPI conversion?
140 width *= 72.0f;
141 height *= 72.0f;
142 // Graphviz and Qt have different conventions for position (top-left vs. center)
143 posX -= width / 2.0f;
144 posY -= height / 2.0f;
145
146 GraphvizLayoutedNode layoutedNode;
147 layoutedNode.posX = posX;
148 layoutedNode.posY = posY;
149 layoutedNode.width = width;
150 layoutedNode.height = height;
151 layoutedNode.label = labelString;
152
153 layouted.nodes.emplace(id, layoutedNode);
154 }
155
156 for (auto& pair : id2edge)
157 {
158 std::pair<int, int> sourceTarget = pair.first;
159 edge_t* edge = pair.second;
160 std::string posString = agget(edge, GRAPHVIZ_ATTR_POS);
161 std::string label = agget(edge, GRAPHVIZ_ATTR_LABEL);
162 std::string labelPos = "";
163 if (char* lp = agget(edge, GRAPHVIZ_ATTR_LABEL_POS))
164 {
165 labelPos = lp;
166 }
167
169
170 GraphvizLayoutedEdge layoutedEdge;
171 layoutedEdge.controlPoints = spline.controlPointList();
172 if (spline.endPoint)
173 {
174 layoutedEdge.endPoint = *spline.endPoint;
175 }
176 if (spline.startPoint)
177 {
178 layoutedEdge.startPoint = *spline.startPoint;
179 }
180 if (label.size() > 0)
181 {
182 std::vector<std::string> split = simox::alg::split(labelPos, ",");
183 if (split.size() == 2)
184 {
185 layoutedEdge.label = label;
186 layoutedEdge.labelPosX = std::stof(split[0]);
187 layoutedEdge.labelPosY = std::stof(split[1]);
188 }
189 }
190 layouted.edges.emplace(sourceTarget, layoutedEdge);
191 }
192
193 return layouted;
194 }
195
196
197} // namespace armarx
constexpr T c
static SupportPoints convertToSpline(const std::string &graphVizSplineType)
GraphvizLayoutedGraph finish(std::string const &savePNG="")
void addNode(int id, std::string const &label)
void addEdge(int sourceID, int targetID, const std::string &label)
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::vector< std::string > split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
std::optional< QPointF > startPoint
std::optional< QPointF > endPoint
std::map< std::pair< int, int >, GraphvizLayoutedEdge > edges
std::map< int, GraphvizLayoutedNode > nodes
QList< QPointF > controlPointList() const