annotate toolboxes/graph_visualisation/share/graphviz/doc/addingLayout.txt @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
rev   line source
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