wolffd@0
|
1 To create a new layout plugin called xxx, you first need
|
wolffd@0
|
2 to provide two functions: xxx_layout and xxx_cleanup. The
|
wolffd@0
|
3 semantics of these are described below.
|
wolffd@0
|
4
|
wolffd@0
|
5 ========================
|
wolffd@0
|
6
|
wolffd@0
|
7 void xxx_layout(Agraph_t * g)
|
wolffd@0
|
8
|
wolffd@0
|
9 Initialize the graph.
|
wolffd@0
|
10 - If the algorithm will use the common edge routing code, it should
|
wolffd@0
|
11 call setEdgeType (g, ...);
|
wolffd@0
|
12
|
wolffd@0
|
13 - For each node, call common_init_node and gv_nodesize.
|
wolffd@0
|
14
|
wolffd@0
|
15 If the algorithm will use spline_edges() to route the edges, the
|
wolffd@0
|
16 node coordinates need to be stored in ND_pos, so this should be
|
wolffd@0
|
17 allocated here. This, and the two calls mentioned above, are all
|
wolffd@0
|
18 handled by a call to neato_init_node().
|
wolffd@0
|
19
|
wolffd@0
|
20 - For each edge, call common_init_edge
|
wolffd@0
|
21
|
wolffd@0
|
22 - The algorithm should allocate whatever other data structures it
|
wolffd@0
|
23 needs. This can involve fields in the A*info_t fields. In addition,
|
wolffd@0
|
24 each of these fields contains a void* alg; subfield that the algorithm
|
wolffd@0
|
25 can use the store additional data.
|
wolffd@0
|
26 Once we move to cgraph, this will all be replace with
|
wolffd@0
|
27 algorithm specific records.
|
wolffd@0
|
28
|
wolffd@0
|
29 Layout the graph. When finished, each node should have its coordinates
|
wolffd@0
|
30 stored in points in ND_coord_i(n), each edge should have its layout
|
wolffd@0
|
31 described in ED_spl(e).
|
wolffd@0
|
32 (N.B. As of version 2.21, ND_coord_i has been replaced by ND_coord,
|
wolffd@0
|
33 which are now floating point coordinates.)
|
wolffd@0
|
34
|
wolffd@0
|
35 To add edges, there are 3 functions available:
|
wolffd@0
|
36
|
wolffd@0
|
37 - spline_edges1 (Agraph_t*, int edgeType)
|
wolffd@0
|
38 Assumes the node coordinates are stored in ND_coord_i, and that
|
wolffd@0
|
39 GD_bb is set. For each edge, this function constructs the appropriate
|
wolffd@0
|
40 data and stores it in ED_spl.
|
wolffd@0
|
41 - spline_edges0 (Agraph_t*)
|
wolffd@0
|
42 Assumes the node coordinates are stored in ND_pos, and that
|
wolffd@0
|
43 GD_bb is set. This function uses the ratio attribute if set,
|
wolffd@0
|
44 copies the values in ND_pos to ND_coord_i (converting from
|
wolffd@0
|
45 inches to points); and calls spline_edges1 using the edge type
|
wolffd@0
|
46 specified by setEdgeType().
|
wolffd@0
|
47 - spline_edges (Agraph_t*)
|
wolffd@0
|
48 Assumes the node coordinates are stored in ND_pos. This
|
wolffd@0
|
49 function calculates the bounding box of g and stores it in GD_bb,
|
wolffd@0
|
50 then calls spline_edges0().
|
wolffd@0
|
51
|
wolffd@0
|
52 If the algorithm only works with connected components, the code can
|
wolffd@0
|
53 use the pack library to get components, lay them out individually, and
|
wolffd@0
|
54 pack them together based on user specifications. A typical schema is
|
wolffd@0
|
55 given below. One can look at the code for twopi, circo, neato or fdp
|
wolffd@0
|
56 for more detailed examples.
|
wolffd@0
|
57
|
wolffd@0
|
58 Agraph_t **ccs;
|
wolffd@0
|
59 Agraph_t *sg;
|
wolffd@0
|
60 Agnode_t *c = NULL;
|
wolffd@0
|
61 int ncc;
|
wolffd@0
|
62 int i;
|
wolffd@0
|
63
|
wolffd@0
|
64 ccs = ccomps(g, &ncc, 0);
|
wolffd@0
|
65 if (ncc == 1) {
|
wolffd@0
|
66 /* layout nodes of g */
|
wolffd@0
|
67 adjustNodes(g); /* if you need to remove overlaps */
|
wolffd@0
|
68 spline_edges(g); /* generic edge routing code */
|
wolffd@0
|
69
|
wolffd@0
|
70 } else {
|
wolffd@0
|
71 pack_info pinfo;
|
wolffd@0
|
72 pack_mode pmode = getPackMode(g, l_node);
|
wolffd@0
|
73
|
wolffd@0
|
74 for (i = 0; i < ncc; i++) {
|
wolffd@0
|
75 sg = ccs[i];
|
wolffd@0
|
76 /* layout sg */
|
wolffd@0
|
77 adjustNodes(sg); /* if you need to remove overlaps */
|
wolffd@0
|
78 }
|
wolffd@0
|
79 spline_edges(g); /* generic edge routing */
|
wolffd@0
|
80
|
wolffd@0
|
81 /* initialize packing info, e.g. */
|
wolffd@0
|
82 pinfo.margin = getPack(g, CL_OFFSET, CL_OFFSET);
|
wolffd@0
|
83 pinfo.doSplines = 1;
|
wolffd@0
|
84 pinfo.mode = pmode;
|
wolffd@0
|
85 pinfo.fixed = 0;
|
wolffd@0
|
86 packSubgraphs(ncc, ccs, g, &pinfo);
|
wolffd@0
|
87 }
|
wolffd@0
|
88 for (i = 0; i < ncc; i++) {
|
wolffd@0
|
89 agdelete(g, ccs[i]);
|
wolffd@0
|
90 }
|
wolffd@0
|
91
|
wolffd@0
|
92 free(ccs);
|
wolffd@0
|
93
|
wolffd@0
|
94 Be careful in laying of subgraphs if you rely on attributes that have
|
wolffd@0
|
95 only been set in the root graph. With connected components, edges can
|
wolffd@0
|
96 be added with each component, before packing (as above) or after the
|
wolffd@0
|
97 components have been packed (see circo).
|
wolffd@0
|
98
|
wolffd@0
|
99 It good to check for trivial cases where the graph has 0 or 1 nodes,
|
wolffd@0
|
100 or no edges.
|
wolffd@0
|
101
|
wolffd@0
|
102 At the end of xxx_layout, call
|
wolffd@0
|
103
|
wolffd@0
|
104 dotneato_postprocess(g);
|
wolffd@0
|
105
|
wolffd@0
|
106 The following template will work in most cases, ignoring the problems of
|
wolffd@0
|
107 handling disconnected graphs and removing node overlaps:
|
wolffd@0
|
108
|
wolffd@0
|
109 static void
|
wolffd@0
|
110 xxx_init_node(node_t * n)
|
wolffd@0
|
111 {
|
wolffd@0
|
112 neato_init_node(n);
|
wolffd@0
|
113 /* add algorithm-specific data, if desired */
|
wolffd@0
|
114 }
|
wolffd@0
|
115
|
wolffd@0
|
116 static void
|
wolffd@0
|
117 xxx_init_edge(edge_t * e)
|
wolffd@0
|
118 {
|
wolffd@0
|
119 common_init_edge(e);
|
wolffd@0
|
120 /* add algorithm-specific data, if desired */
|
wolffd@0
|
121 }
|
wolffd@0
|
122
|
wolffd@0
|
123 static void
|
wolffd@0
|
124 xxx_init_node_edge(graph_t * g)
|
wolffd@0
|
125 {
|
wolffd@0
|
126 node_t *n;
|
wolffd@0
|
127 edge_t *e;
|
wolffd@0
|
128
|
wolffd@0
|
129 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
|
wolffd@0
|
130 xxx_init_node(n);
|
wolffd@0
|
131 }
|
wolffd@0
|
132 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
|
wolffd@0
|
133 for (e = agfstout(g, n); e; e = agnxtout(g, e)){
|
wolffd@0
|
134 xxx_init_edge(e);
|
wolffd@0
|
135 }
|
wolffd@0
|
136 }
|
wolffd@0
|
137 }
|
wolffd@0
|
138
|
wolffd@0
|
139 void
|
wolffd@0
|
140 xxx_layout (Agraph_t* g)
|
wolffd@0
|
141 {
|
wolffd@0
|
142 xxx_init_node_edge(g);
|
wolffd@0
|
143 /* Set ND_pos(n) for each node n */
|
wolffd@0
|
144 spline_edges(g);
|
wolffd@0
|
145 dotneato_postprocess(g);
|
wolffd@0
|
146 }
|
wolffd@0
|
147
|
wolffd@0
|
148 ======================
|
wolffd@0
|
149
|
wolffd@0
|
150 void xxx_cleanup(Agraph_t * g)
|
wolffd@0
|
151
|
wolffd@0
|
152 Free up any resources allocated in the layout.
|
wolffd@0
|
153
|
wolffd@0
|
154 Finish with calls to gv_cleanup_node and gv_cleanup_edge for
|
wolffd@0
|
155 each node and edge. This cleans up splines labels, ND_pos, shapes
|
wolffd@0
|
156 and 0's out the A*info_t, so these have to occur last, but could be
|
wolffd@0
|
157 part of explicit xxx_cleanup_node and xxx_cleanup_edge, if desired.
|
wolffd@0
|
158 At the end, you should do
|
wolffd@0
|
159
|
wolffd@0
|
160 if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t));
|
wolffd@0
|
161
|
wolffd@0
|
162 This is necessary for the graph to be laid out again, as the layout
|
wolffd@0
|
163 code assumes this structure is clean.
|
wolffd@0
|
164
|
wolffd@0
|
165 libgvc does a final cleanup to the root graph, freeing any drawing,
|
wolffd@0
|
166 freeing its label, and zeroing out Agraphinfo_t of the root graph.
|
wolffd@0
|
167
|
wolffd@0
|
168 The following template will work in most cases:
|
wolffd@0
|
169
|
wolffd@0
|
170 static void xxx_cleanup_graph(Agraph_t * g)
|
wolffd@0
|
171 {
|
wolffd@0
|
172 /* Free any algorithm-specific data attached to the graph */
|
wolffd@0
|
173 if (g != g->root) memset(&(g->u), 0, sizeof(Agraphinfo_t));
|
wolffd@0
|
174 }
|
wolffd@0
|
175
|
wolffd@0
|
176 static void xxx_cleanup_edge (Agedge_t* e)
|
wolffd@0
|
177 {
|
wolffd@0
|
178 /* Free any algorithm-specific data attached to the edge */
|
wolffd@0
|
179 gv_cleanup_edge(e);
|
wolffd@0
|
180 }
|
wolffd@0
|
181
|
wolffd@0
|
182 static void xxx_cleanup_node (Agnode_t* n)
|
wolffd@0
|
183 {
|
wolffd@0
|
184 /* Free any algorithm-specific data attached to the node */
|
wolffd@0
|
185 gv_cleanup_node(e);
|
wolffd@0
|
186 }
|
wolffd@0
|
187
|
wolffd@0
|
188 void xxx_cleanup(Agraph_t * g)
|
wolffd@0
|
189 {
|
wolffd@0
|
190 Agnode_t *n;
|
wolffd@0
|
191 Agedge_t *e;
|
wolffd@0
|
192
|
wolffd@0
|
193 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
|
wolffd@0
|
194 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
|
wolffd@0
|
195 xxx_cleanup_edge(e);
|
wolffd@0
|
196 }
|
wolffd@0
|
197 xxx_cleanup_node(n);
|
wolffd@0
|
198 }
|
wolffd@0
|
199 xxx_cleanup_graph(g);
|
wolffd@0
|
200 }
|
wolffd@0
|
201
|
wolffd@0
|
202 ==================
|
wolffd@0
|
203
|
wolffd@0
|
204 Most layouts use auxiliary routines similar to neato, so
|
wolffd@0
|
205 the entry points can be added in plugin/neato_layout
|
wolffd@0
|
206
|
wolffd@0
|
207 Add to gvlayout_neato_layout.c:
|
wolffd@0
|
208
|
wolffd@0
|
209 gvlayout_engine_t xxxgen_engine = {
|
wolffd@0
|
210 xxx_layout,
|
wolffd@0
|
211 xxx_cleanup,
|
wolffd@0
|
212 };
|
wolffd@0
|
213
|
wolffd@0
|
214 and the line
|
wolffd@0
|
215
|
wolffd@0
|
216 {LAYOUT_XXX, "xxx", 0, &xxxgen_engine, &neatogen_features},
|
wolffd@0
|
217
|
wolffd@0
|
218 to gvlayout_neato_types and a new emum
|
wolffd@0
|
219
|
wolffd@0
|
220 LAYOUT_XXX
|
wolffd@0
|
221
|
wolffd@0
|
222 to layout_type in that file.
|
wolffd@0
|
223
|
wolffd@0
|
224 The above allows the new layout to piggyback on top of the neato
|
wolffd@0
|
225 plugin, but requires rebuilding the plugin. In general, a user
|
wolffd@0
|
226 can (and probably should) build a layout plugin totally separately.
|
wolffd@0
|
227
|
wolffd@0
|
228 To do this, after writing xxx_layout and xxx_cleanup, it is necessary to:
|
wolffd@0
|
229
|
wolffd@0
|
230 - add the types and data structures
|
wolffd@0
|
231
|
wolffd@0
|
232 typedef enum { LAYOUT_XXX } layout_type;
|
wolffd@0
|
233
|
wolffd@0
|
234 static gvlayout_features_t xxxgen_features = {
|
wolffd@0
|
235 0
|
wolffd@0
|
236 };
|
wolffd@0
|
237 gvlayout_engine_t xxxgen_engine = {
|
wolffd@0
|
238 xxx_layout,
|
wolffd@0
|
239 xxx_cleanup,
|
wolffd@0
|
240 };
|
wolffd@0
|
241 static gvplugin_installed_t gvlayout_xxx_types[] = {
|
wolffd@0
|
242 {LAYOUT_XXX, "xxx", 0, &xxxgen_engine, &xxxgen_features},
|
wolffd@0
|
243 {0, NULL, 0, NULL, NULL}
|
wolffd@0
|
244 };
|
wolffd@0
|
245 static gvplugin_api_t apis[] = {
|
wolffd@0
|
246 {API_layout, &gvlayout_xxx_types},
|
wolffd@0
|
247 {(api_t)0, 0},
|
wolffd@0
|
248 };
|
wolffd@0
|
249 gvplugin_library_t gvplugin_xxx_layout_LTX_library = { "xxx_layout", apis };
|
wolffd@0
|
250
|
wolffd@0
|
251 - combine all of this into a dynamic library whose name contains the
|
wolffd@0
|
252 string "gvplugin_" and install the library in the same directory as the
|
wolffd@0
|
253 other Graphviz plugins. For example, on Linux systems, the dot layout
|
wolffd@0
|
254 plugin is in the library libgvplugin_dot_layout.so.
|
wolffd@0
|
255
|
wolffd@0
|
256 - run
|
wolffd@0
|
257 dot -c
|
wolffd@0
|
258 to regenerate the config file.
|
wolffd@0
|
259
|
wolffd@0
|
260 NOTES:
|
wolffd@0
|
261 - Additional layouts can be added as extra lines in gvlayout_xxx_types.
|
wolffd@0
|
262 - Obviously, most of the names and strings can be arbitrary. One
|
wolffd@0
|
263 constraint is that external identifier for the gvplugin_library_t
|
wolffd@0
|
264 type must end in "_LTX_library". In addition, the string "xxx" in
|
wolffd@0
|
265 each entry of gvlayout_xxx_types is the name used to identify the
|
wolffd@0
|
266 layout algorithm, so needs to be distinct from any other layout name.
|
wolffd@0
|
267 - The features of a layout algorithm are currently limited to a
|
wolffd@0
|
268 flag of bits, and the only flag supported is LAYOUT_USES_RANKDIR,
|
wolffd@0
|
269 which enables the layout to the rankdir attribute.
|
wolffd@0
|
270
|
wolffd@0
|
271 Changes need to be made to any applications, such as gvedit, that
|
wolffd@0
|
272 statically know about layout algorithms.
|
wolffd@0
|
273
|
wolffd@0
|
274 ==================
|
wolffd@0
|
275
|
wolffd@0
|
276 Software configuration - automake
|
wolffd@0
|
277
|
wolffd@0
|
278 If you want to integrate your code into the Graphviz software
|
wolffd@0
|
279 and use its build system, follow the instructions below.
|
wolffd@0
|
280 You can certainly build and install your plugin using your own
|
wolffd@0
|
281 build software.
|
wolffd@0
|
282
|
wolffd@0
|
283 0. Put your software in lib/xxxgen, and added the hooks describe above
|
wolffd@0
|
284 into gvlayout_neato_layout.c
|
wolffd@0
|
285 1. In lib/xxxgen, provide a Makefile.am (based on a simple example
|
wolffd@0
|
286 like lib/fdpgen/Makefile.am)
|
wolffd@0
|
287 3. In lib/Makefile.am, add xxxgen to SUBDIRS
|
wolffd@0
|
288 2. In configure.ac, add lib/xxxgen/Makefile to AC_CONFIG_FILES.
|
wolffd@0
|
289 4. In lib/plugin/neato_layout/Makefile.am, insert
|
wolffd@0
|
290 $(top_builddir)/lib/xxxgen/libxxxgen_C.la
|
wolffd@0
|
291 in libgvplugin_neato_layout_C_la_LIBADD
|
wolffd@0
|
292 5. Remember to run autogen.sh because on its own configure can guess wrong.
|
wolffd@0
|
293
|
wolffd@0
|
294 This also assumes you have a good version of the various automake tools
|
wolffd@0
|
295 on your system.
|
wolffd@0
|
296
|
wolffd@0
|
297
|