1 | /* $Id$ $Revision$ */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | |
15 | /* |
16 | * Circular layout. Biconnected components are put on circles. |
17 | * block-cutnode tree is done recursively, with children placed |
18 | * about parent block. |
19 | * Based on: |
20 | * Six and Tollis, "A Framework for Circular Drawings of |
21 | * Networks", GD '99, LNCS 1731, pp. 107-116; |
22 | * Six and Tollis, "Circular Drawings of Biconnected Graphs", |
23 | * Proc. ALENEX '99, pp 57-73. |
24 | * Kaufmann and Wiese, "Maintaining the Mental Map for Circular |
25 | * Drawings", GD '02, LNCS 2528, pp. 12-22. |
26 | */ |
27 | |
28 | #include "circular.h" |
29 | #include "adjust.h" |
30 | #include "pack.h" |
31 | #include "neatoprocs.h" |
32 | #include <string.h> |
33 | |
34 | static void circular_init_edge(edge_t * e) |
35 | { |
36 | agbindrec(e, "Agedgeinfo_t" , sizeof(Agedgeinfo_t), TRUE); //node custom data |
37 | common_init_edge(e); |
38 | |
39 | ED_factor(e) = late_double(e, E_weight, 1.0, 0.0); |
40 | } |
41 | |
42 | |
43 | static void circular_init_node_edge(graph_t * g) |
44 | { |
45 | node_t *n; |
46 | edge_t *e; |
47 | int i = 0; |
48 | ndata* alg = N_NEW(agnnodes(g), ndata); |
49 | |
50 | GD_neato_nlist(g) = N_NEW(agnnodes(g) + 1, node_t *); |
51 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
52 | neato_init_node(n); |
53 | ND_alg(n) = alg + i; |
54 | GD_neato_nlist(g)[i++] = n; |
55 | } |
56 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
57 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) { |
58 | circular_init_edge(e); |
59 | } |
60 | } |
61 | } |
62 | |
63 | |
64 | void circo_init_graph(graph_t * g) |
65 | { |
66 | setEdgeType (g, ET_LINE); |
67 | /* GD_ndim(g) = late_int(g,agfindattr(g,"dim"),2,2); */ |
68 | Ndim = GD_ndim(g) = 2; /* The algorithm only makes sense in 2D */ |
69 | circular_init_node_edge(g); |
70 | } |
71 | |
72 | /* makeDerivedNode: |
73 | * Make a node in the derived graph, with the given name. |
74 | * orig points to what it represents, either a real node or |
75 | * a cluster. Copy size info from original node; needed for |
76 | * adjustNodes and packSubgraphs. |
77 | */ |
78 | static node_t *makeDerivedNode(graph_t * dg, char *name, int isNode, |
79 | void *orig) |
80 | { |
81 | node_t *n = agnode(dg, name,1); |
82 | agbindrec(n, "Agnodeinfo_t" , sizeof(Agnodeinfo_t), TRUE); //node custom data |
83 | ND_alg(n) = (void *) NEW(cdata); |
84 | if (isNode) { |
85 | ND_pos(n) = N_NEW(Ndim, double); |
86 | ND_lw(n) = ND_lw((node_t *) orig); |
87 | ND_rw(n) = ND_rw((node_t *) orig); |
88 | ND_ht(n) = ND_ht((node_t *) orig); |
89 | ORIGN(n) = (node_t *) orig; |
90 | } else |
91 | ORIGG(n) = (graph_t *) orig; |
92 | return n; |
93 | } |
94 | |
95 | /* circomps: |
96 | * Construct a strict, undirected graph with no loops from g. |
97 | * Construct the connected components with the provision that all |
98 | * nodes in a block subgraph are considered connected. |
99 | * Return array of components with number of components in cnt. |
100 | * Each component has its blocks as subgraphs. |
101 | * FIX: Check that blocks are disjoint. |
102 | */ |
103 | Agraph_t **circomps(Agraph_t * g, int *cnt) |
104 | { |
105 | int c_cnt; |
106 | Agraph_t **ccs; |
107 | Agraph_t *dg; |
108 | Agnode_t *n, *v, *dt, *dh; |
109 | Agedge_t *e; |
110 | Agraph_t *sg; |
111 | int i; |
112 | Agedge_t *ep; |
113 | Agnode_t *p; |
114 | |
115 | dg = agopen("derived" , Agstrictundirected,NIL(Agdisc_t *)); |
116 | agbindrec (dg, "info" , sizeof(Agraphinfo_t), TRUE); |
117 | GD_alg(g) = dg; /* store derived graph for closing later */ |
118 | |
119 | for (v = agfstnode(g); v; v = agnxtnode(g, v)) { |
120 | if (DNODE(v)) |
121 | continue; |
122 | n = makeDerivedNode(dg, agnameof(v), 1, v); |
123 | DNODE(v) = n; |
124 | } |
125 | |
126 | for (v = agfstnode(g); v; v = agnxtnode(g, v)) { |
127 | for (e = agfstout(g, v); e; e = agnxtout(g, e)) { |
128 | dt = DNODE(agtail(e)); |
129 | dh = DNODE(aghead(e)); |
130 | if (dt != dh) { |
131 | agbindrec(agedge(dg, dt, dh, NULL, 1), "Agedgeinfo_t" , sizeof(Agedgeinfo_t), TRUE); //node custom data |
132 | } |
133 | } |
134 | } |
135 | |
136 | ccs = ccomps(dg, &c_cnt, 0); |
137 | |
138 | /* replace block nodes with block contents */ |
139 | for (i = 0; i < c_cnt; i++) { |
140 | sg = ccs[i]; |
141 | |
142 | /* add edges: since sg is a union of components, all edges |
143 | * of any node should be added, except loops. |
144 | */ |
145 | for (n = agfstnode(sg); n; n = agnxtnode(sg, n)) { |
146 | p = ORIGN(n); |
147 | for (e = agfstout(g, p); e; e = agnxtout(g, e)) { |
148 | /* n = DNODE(agtail(e)); by construction since agtail(e) == p */ |
149 | dh = DNODE(aghead(e)); |
150 | if (n != dh) { |
151 | ep = agedge(dg, n, dh, NULL, 1); |
152 | agbindrec(ep, "Agedgeinfo_t" , sizeof(Agedgeinfo_t), TRUE); //node custom data |
153 | agsubedge(sg,ep,1); |
154 | } |
155 | } |
156 | } |
157 | } |
158 | |
159 | /* Finally, add edge data to edges */ |
160 | for (n = agfstnode(dg); n; n = agnxtnode(dg, n)) { |
161 | for (e = agfstout(dg, n); e; e = agnxtout(dg, e)) { |
162 | ED_alg(e) = NEW(edata); |
163 | } |
164 | } |
165 | |
166 | *cnt = c_cnt; |
167 | return ccs; |
168 | } |
169 | |
170 | /* closeDerivedGraph: |
171 | */ |
172 | static void closeDerivedGraph(graph_t * g) |
173 | { |
174 | node_t *n; |
175 | edge_t *e; |
176 | |
177 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
178 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) { |
179 | free(ED_alg(e)); |
180 | } |
181 | free(ND_alg(n)); |
182 | free(ND_pos(n)); |
183 | } |
184 | agclose(g); |
185 | } |
186 | |
187 | /* copyPosns: |
188 | * Copy position of nodes in given subgraph of derived graph |
189 | * to corresponding node in original graph. |
190 | * FIX: consider assigning from n directly to ORIG(n). |
191 | */ |
192 | static void copyPosns(graph_t * g) |
193 | { |
194 | node_t *n; |
195 | node_t *v; |
196 | |
197 | for (n = agfstnode(g); n; n = agnxtnode(g, n)) { |
198 | v = ORIGN(n); |
199 | ND_pos(v)[0] = ND_pos(n)[0]; |
200 | ND_pos(v)[1] = ND_pos(n)[1]; |
201 | } |
202 | } |
203 | |
204 | /* circoLayout: |
205 | */ |
206 | void circoLayout(Agraph_t * g) |
207 | { |
208 | Agraph_t **ccs; |
209 | Agraph_t *sg; |
210 | int ncc; |
211 | int i; |
212 | |
213 | if (agnnodes(g)) { |
214 | ccs = circomps(g, &ncc); |
215 | |
216 | if (ncc == 1) { |
217 | circularLayout(ccs[0], g); |
218 | copyPosns(ccs[0]); |
219 | adjustNodes(g); |
220 | } else { |
221 | Agraph_t *dg = ccs[0]->root; |
222 | pack_info pinfo; |
223 | getPackInfo(g, l_node, CL_OFFSET, &pinfo); |
224 | |
225 | for (i = 0; i < ncc; i++) { |
226 | sg = ccs[i]; |
227 | circularLayout(sg, g); |
228 | adjustNodes(sg); |
229 | } |
230 | /* FIX: splines have not been calculated for dg |
231 | * To use, either do splines in dg and copy to g, or |
232 | * construct components of g from ccs and use that in packing. |
233 | */ |
234 | packSubgraphs(ncc, ccs, dg, &pinfo); |
235 | for (i = 0; i < ncc; i++) |
236 | copyPosns(ccs[i]); |
237 | } |
238 | free(ccs); |
239 | } |
240 | } |
241 | |
242 | /* circo_layout: |
243 | */ |
244 | void circo_layout(Agraph_t * g) |
245 | { |
246 | if (agnnodes(g) == 0) return; |
247 | circo_init_graph(g); |
248 | circoLayout(g); |
249 | /* Release ND_alg as we need to reuse it during edge routing */ |
250 | free(ND_alg(agfstnode(g))); |
251 | spline_edges(g); |
252 | dotneato_postprocess(g); |
253 | } |
254 | |
255 | /* circo_cleanup: |
256 | * ND_alg is freed in circo_layout |
257 | */ |
258 | void circo_cleanup(graph_t * g) |
259 | { |
260 | node_t *n; |
261 | edge_t *e; |
262 | |
263 | n = agfstnode(g); |
264 | if (n == NULL) |
265 | return; /* g is empty */ |
266 | |
267 | closeDerivedGraph((graph_t*)GD_alg(g)); /* delete derived graph */ |
268 | |
269 | /* free (ND_alg(n)); */ |
270 | for (; n; n = agnxtnode(g, n)) { |
271 | for (e = agfstout(g, n); e; e = agnxtout(g, e)) { |
272 | gv_cleanup_edge(e); |
273 | } |
274 | gv_cleanup_node(n); |
275 | } |
276 | free(GD_neato_nlist(g)); |
277 | if (g != agroot(g)) |
278 | agclean (g,AGRAPH,"Agraphinfo_t" ); |
279 | } |
280 | |