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